出现一个让我很废脑的问题,我有一个接口,使用了ThinkORM,有事务,当请求后,会增加用户的余额。
刚开始启动webman运行,很正常,可以按照正常的流程去运行,数据也能正常的更新到数据库里。
但是过一会,假定为1个小时,我再次请求这个接口,接口能正常返回,返回接口能看到增加后的余额,增加余额后,我再去余额列表查看,数据也能获取到最新增加的余额增加记录。
我反复请求接口,都能正常运行,余额列表也能获取到最新的订单信息,但是奇怪的是,这个数据好像只存在于内存中,只有当前的连接能调用,我用另外的接口去请求最新的余额订单,返回的却是1个小时之前的数据。
我所有接口的读操作正常,所有的写操作都会出现这个问题。
我使用数据库软件,phpMyadmin和HeidiSQL查看数据库,均无法查到我后面接口请求的数据,只能查到1个小时前的数据。
程序是正常运行的,没有任何报错,运行逻辑也很清晰。
但是当我重新启动webman,我请求余额列表的数据,包括用户余额的数据,后面新增的数据都不存在了,请求的都是1个小时前的数据,因为后面的数据根本就没有实际写入到数据库里面。重启webman导致了数据库重新连接。
我使用了redis缓存,但是这个问题不是缓存的问题,我清除缓存,会出现一样的问题,缓存不可能在我重启后,就失效。
我被这个问题困扰了2天,后面我尝试使用webman/log这个插件,分析一下SQL数据,奇怪的是,竟然不会出现之前那种,请求正常,但是不会实际写入数据库这种情况了,竟然一切都恢复正常了。
当我将webman/log设置成false,不运行的时候,问题又会出现。
我之前也怀疑是事务没提交,但是根据运行流程,事务是肯定运行了,并且也能成功返回数据,只要我不重启webman,这些数据会一直存在,也会新增,只不过,只要我重启webman,一切都会回到之前。
因为webman/log会产生大量的日志,调试的时候能用一下,但是生产的时候不想使用,我想知道可能出现这个问题的原因,或者解决办法,有没有大神能告知一二,万分感谢,下面有一些具体的信息。
我论坛里面找到了二个类似的贴子
https://www.workerman.net/q/8570
https://www.workerman.net/q/7476
我使用的库
"require": {
"php": ">=7.2",
"workerman/webman-framework": "^1.5.0",
"monolog/monolog": "^2.0",
"tinywan/exception-handler": "^1.1",
"webman/think-orm": "^1.0",
"psr/container": "^2.0",
"illuminate/redis": "^10.5",
"symfony/cache": "^6.2",
"webman/console": "^1.2",
"topthink/think-log": "^2.0",
"workerman/crontab": "^1.0",
"kreait/firebase-php": "^7.2",
"zjkal/time-helper": "^1.1",
"tinywan/validate": "^0.0.6",
"ext-openssl": "*",
"yzh52521/webman-lock": "^1.0",
"ext-pdo": "*",
"yzh52521/webman-throttle": "^1.0",
"google/apiclient": "^2.13",
"symfony/translation": "^6.2",
"webman/redis-queue": "^1.2",
"vlucas/phpdotenv": "^5.5",
"webman/log": "^1.1"
}
运行伪代码
// 增加硬币,然后写入订单凭证
$call_info = $request->all();
$datedCoins = UserModel::getCacheUserInfo($call_info['user_id'], 'coins') ?? 0;
Log::error('开始事务');
Db::startTrans();
try {
$mysql_data = [
'user_id' => $call_info['user_id'],
'coins' => $call_info['reward_amount'],
'dated_coins' => $datedCoins,
'type' => 1,
];
// 将硬币更新到用户表,增加硬币
Log::error('准备更新user表');
$result = (new UserModel())->where('user_id', '=', $call_info['user_id'])->cache(3600)->update(['coins' => ['inc', $call_info['coins']]]);
Log::error('user表更新成功');
if ($result){
// 写入订单表
Log::error('user表硬币增加成功,开始写入订单表');
(new AdOrderModel())->create($mysql_data);
Log::error('订单表写入成功,准备提交事务');
Db::commit();
Log::error('事务提交成功');
return show('Success');
}else{
Db::rollback();
Log::error('coins增加失败');
return show('fail');
}
} catch (DbException $e) {
Db::rollback();
Log::error('coins增加失败');
} catch (\Exception $e) {
Db::rollback();
Log::error('coins增加失败');
} finally {
Log::error('广告硬币增加成功--------------------');
}
打印请求流程,有异常前和没有异常前都能正常执行这个流程。
[2023-06-08 09:37:27][error]:开始事务
[2023-06-08 09:37:27][error]:准备更新user表
[2023-06-08 09:37:27][error]:user表更新成功
[2023-06-08 09:37:27][error]:user表硬币增加成功,开始写入订单表
[2023-06-08 09:37:27][error]:订单表写入成功,准备提交事务
[2023-06-08 09:37:27][error]:事务提交成功
[2023-06-08 09:37:27][error]:返回成功
[2023-06-08 09:37:27][error]:广告硬币增加成功--------------------
应该是事务没提交。安装 webman/log 后会自动检测未提交的事务,回滚并记录日志。
在runtime/logs/下的日志里找下ERROR 或 Uncommitted transaction关键字,看下是哪个请求没提交事务。
webman/log是否可以增加一个配置项,可以配置是否启用日志记录?
自己改下插件启用状态,或者自己弄个变量,是否启用插件就行了
确实是这样,我解决了Uncommitted transaction found and try to rollback的异常后,暂时还没有发现问题。
找到了,这个插件自带有配置项
文档有说吗?一直都是DB类开启里面也会有model交叉。。
自己试试就知道了
刚测试有效啊。。try里面用model 更新数据,然后后面报错,回滚,数据也没提交成功
开启用的Db 开启
默认都有效。
不过模型如果设置了connection要注意下,Db使用的时候也要指定相同的connection
老大揭秘了,如果是多个connection要在同一个connection下,目前都是默认,,
感谢指点。
这种情况,多数是事务没提交
感谢指点,正是事务的问题,使用webman/log插件后,会有提示异常,解决掉异常问题就解决了。
遇到过类似问题,事务没提交,回滚了。
解决方法
catch (\Exception $e)
改成
模型没办法用事务吗,测试没问题啊
模型可以事务
感谢指点。
mark~
是因为Exception 没捕获到异常吗
因为事务没有提交,会出现事务错误,但是这个在当前请求不会出问题,会影响后面事务的运行,一直报错,安装webman/log后,会自动检测并回滚事务,并会给出错误提示,你根据提示,找到没运行事务的入口,解决掉,之前所有的问题就都解决了。
哦哦,webman/log 还会自动回滚事务。。以为只能记录。。