文章
· 九月 29, 2022 阅读大约需 3 分钟

Cache死循环检测和申明式事务

使用Cache的两个痛点问题:

一、Cache作为后关系型数据库,使用其提供M语言操作Global数据结构。可以达到极快的查询速度。M语言的set赋值和初始化没有区分,加之是弱类型,非常容易出现死循环,就算你是多年老手也一个不小心就踩坑。我就经历过改代码即使很小心还是出了死循环把数据库tmp撑满的事故。

二、M语言作为完备的编程语言,结合cache数据库操作数据实在是不要太方便。同时一个复杂的业务提交到数据库通过一个M逻辑全部处理完成,极大的减少了app和数据库交互次数。复杂业务的事务就是一方面,通过TSTART、TROLLBACK 、TCOMMIT,前台一次数据库交互后台就可能做了几十张表的更新操作。直接使用事务是容易,但是极易引入开放事务锁表(你自己就算很小心,也难保调别人接口别人事务不完善,而你又没检测事务层级),锁表之后结束进程导致数据回滚。

 

解决办法:

据我观察出现上面两个问题的原因,第一个是M语言弱类型和没单独的初始化命令导致,就算你是老手、工作细致也难以避免。第二个不是Cache特有的,通用关系库也有事务写的不完整的问题,事务问题对开发要求高或者从框架设计可以解决。

 

可以基于AOP切面设计一套死循环和开放事务避免体系,从架构上解决死循环难以发现引起的数据库崩溃和开放事务问题。

首先要求程序框架有统一的调入数据库入口,才方便在入口统一实现死循环监测和开放事务监测。

死循环处理分为以下几个要点:
1.在程序调用数据库M的入口处记住当前进程号和时间以及参数
2.在程序调用数据库M的返回出口前删除上面记录的当前进程的信息
3.在执行要调用的具体类方法前遍历没被删除的记录的进程信息,检测时间超过10分钟的进程结束(用户界面哪有执行10分钟还没响应的,把结束的进程记录进入杀进程信息中)

 

开放事务处理分为以下几个要点:

1.对新代码提供托管事务方法来托管事务

2.对已有逻辑懒得改的部分,在框架调数据库返回地方统一判断事务层级,层级大于1的统一回滚,并且把调用类和方法以报错抛出去,防止开放事务积累。

 

实际改造中因为我们用到了“InterSystems.Data.CacheClient.dll”,而通过ado执行存储过程的M没有统一入口,那么存储过程部分如果不改造就无法统一处理死循环和开放事务。为此用M实现了和“InterSystems.Data.CacheClient.dll”同级的ado库,实现微软ado接口来处理存储过程部分。实现参照Ado.net改为M实现参照

 

这样改造之后使用Cache库就达到比较完美的状态了,享受了M和global的贴心结合,又不用担心容易出死循环和事务锁表及回滚数据。此改造来自一个晚上做的梦,第二天马上就给实现了,引入系统发布之后系统稳定性得到提升。对老项目碰到棘手的锁表问题查不到原因的,我还依赖这个更新解决几个项目锁表和回滚数据问题。

 

实现参照:死循环和托管事务参照

 

托管事务用法参照:托管事务使用示例

 

讨论 (4)4
登录或注册以继续