环境
我工作的公司已经编写了一个解决方案,这样我们就可以通过将API调用放入一个表中来将它们发送到数据库外部并对其进行排队。我使用这个解决方案从Jira获取一些信息,这些信息随后被插入到数据库中,这样我们就可以将相关数据放入表中并在前端使用它。我已经编写了以下对象类型来处理API调用的大多数行为:
- O_API
- O_请求处理程序
- O_响应_处理程序
基本上O_API对象类型接受O_REQUEST_HANDLER和O_RESPONSE_HANDLER作为参数。因此,从O_REQUEST_HANDLER调用方法来准备和发送请求。然后返回响应,然后由O_RESPONSE_HANDLER处理。
我创建了这些对象类型,因为我们有许多不同的API调用需要实现,并且每个对象类型在准备请求和处理响应时的行为都不同。使用这个方法,我可以创建O_REQUEST_HANDLER和O_RESPONSE_HANDLER的子类型,并且仍然将它们传递给O_API,O_API通过多态性的魔力调用所需的方法。(可能有一个更适合的设计模式,但我只是刚刚学习的战略模式,因为我仍然是一个学生和实习生在我上面提到的公司)。一个例子,这是如何在一个过程中使用:
-- creating the request and response handler
l_request_handler := o_jira_get_issues_request_handler(l_startAt, l_lastupdated, l_boardId, l_maxresults);
l_response_handler := o_jira_get_issues_response_handler(l_lastupdated, l_boardId, l_getpagesconcurrently);
-- creating the api object
l_jira_api := o_api('Get Jira Issues (& Comments) API', l_request_handler, l_response_handler);
-- calling the procedure to handle the request
l_jira_api.p_processRequest;
调用API类型的所有过程都在一个包中定义,该包只有局部变量,没有定义包范围的变量。
问题
问题是,一旦API调用开始从队列中执行,有时会返回错误,指出存在无效对象(队列基本上是一个表,引用汇编和调用相关API调用的过程)。
ORA-04061: existing state of has been invalidated
ORA-06512: at SCHEMA.PKG_JIRA, line 112
ORA-06512: at line 1
ORA-06512: at SCHEMA.PKG_QUEUE, line 309
抛出假定错误的行是在创建O_REQUEST_HANDLER子类型的时刻(在这种情况下,该行、其他API调用也抛出相同的错误):
l_request_handler := o_jira_get_issues_request_handler(l_startAt, l_lastupdated, l_boardId, l_maxresults);
型
这只发生在我添加了我最新的API调用之后,所以我很困惑为什么会发生这种情况,我应该指出,这种情况也不会发生在每个API调用中(即使当它们调用完全相同的过程来调用具有相同请求和响应处理程序类型的API调用时),这似乎是随机发生的,抛出的错误比不抛出的多,所以现在不抛出错误的几个API调用是例外(无意双关)。
我还必须提到,当我手动调用处理API调用的过程时,它从不抛出错误,只有当它被运行队列中所有不同条目的作业调用时才会抛出错误。
我尝试过的(但没有成功)
- 我重新编译了我所有的对象类型和对象类型主体(如果它们有依赖关系,我注意到这是一种痛苦)。
- 我还通过查询all_objects表来检查是否仍然存在无效的对象类型,实际上并不存在
- 删除了我的API调用的实现,之后错误开始出现。
- request_handler有另一个对象属性request_t,它保存了所有的请求信息。我还手动重新编译了这个属性,看看它是否能解决任何问题。
- 手动调用创建、发送和处理API调用的过程,正如我之前所说的,这可以正常工作,但只有当调用队列由外部作业运行时才会抛出错误。
- 谷歌错误的解决方案,按照指示,但他们不会帮助不幸的。
我希望这是足够的信息,任何人都得到一个想法的问题可能是什么,我知道我没有显示很多代码,但该公司禁止我这样做不幸的是,感谢阅读,我将期待着你的答案!
1条答案
按热度按时间eqfvzcg81#
这个错误并不意味着存在任何无效对象,因此您的查询不应该显示任何无效对象。
这是由有状态程序包引起的:
如果PL/SQL程序包至少声明了一个变量、常量或游标,则该程序包是有状态的;否则就是无国籍的。
您说“没有定义包范围的变量”,但错误表明一定有什么东西使它有状态。
引用程序包项的每个会话都有其自己的程序包示例化。如果程序包是有状态的,则示例化包括其状态。
您在重新编译程序包之后看到了该错误,最初是为了向其中添加新的API(过程)。您看到的行为表明队列进程具有现有的数据库会话(可能是来自连接池的多个会话),并且以前引用该程序包的每个会话都具有状态。
程序包状态在会话的整个生命周期内保持不变,但以下情况除外:
如果重新编译示例化的有状态程序包的主体(使用“ALTER PACKAGE语句”显式编译或隐式编译),则下次调用程序包中的子程序时,Oracle数据库将放弃现有程序包状态并引发异常错误ORA-04068。
PL/SQL引发异常错误后,对程序包的引用将导致Oracle数据库重新示例化程序包,从而重新初始化程序包。因此,以前对程序包状态所做的更改将丢失。
这仅指ORA-04068,但ORA-04061属于同一类问题。
我认为你可能有一个连接池的原因是错误并不总是发生,你在添加新过程后进行的第一次调用可能会出错,但是所使用的会话会有新的状态,并且如果你碰巧重用它,也不会再次出错;但是如果你的队列从连接池中选择了一个不同的连接,那么 * 它的 * 会话第一次就会出错,但是之后就会恢复正常,以此类推。
您尝试的事情只是扩展了问题--重新编译所有内容,或者只是那个包来再次删除新过程,将放弃已经遇到的会话上的状态并清 debugging 误,因此它们将再次命中它。
最终所有的会话都将遇到错误,然后处于干净状态,但这可能需要一段时间(取决于池大小和循环所有连接所需的时间);但你试图修复它却没有给予它机会。
当然,仅仅遇到错误并强行通过它们是不理想的。您还没有说明队列是如何操作的,但如果您使用中间件(如WebLogic),通常有一种方法可以重置连接池,即关闭所有打开的连接并以全新状态启动新的连接。
如果您知道您有状态包,那么执行重置,或者如果不可能,那么可能重新启动应用程序/队列,这可能需要成为您在部署对包的更改时使用的过程的一部分。
当您手动调用将处于连接池之外的新会话中的包时,它将具有自己的新鲜状态-因此不会出错,因为没有要丢弃的预编译状态。
如果可以的话,可能值得检查所有的包和其他对象,看看是什么使它们具有状态,并看看这是否真的必要。