【问题标题】:Cannot Persist Further Operations After Rolling Back a Dry Run Transaction回滚试运行事务后无法继续执行进一步操作
【发布时间】:2021-07-15 10:59:45
【问题描述】:

我有一个工匠命令,我正在清理一些已经变坏的数据。在我实际删除数据之前,我想进行一次试运行,并展示删除该数据可能带来的一些影响。

我的命令的本质是:

    public function handle()
    {
        ...

        $this->dryRun($modelsToDelete); // Prints info to user

        if ($this->confirm('Are you sure you want to delete?') {
            $modelsToDelete->each->forceDelete();
        }
        ...
    }

    public function dryRun($modelsToDelete)
    {
        ...

        DB::connection($connection)->beginTransaction();
        $before = $this->findAllOrphans($models);

        $modelsToDelete->each(function ($record) use ($bar) {
            $record->forceDelete();
        });

        $after = $this->findAllOrphans($models);
        DB::connection($connection)->rollBack();

        // Print info about diff
        ...
    }

问题是当我做空运行并确认删除时,实际操作并没有持久化在数据库中。如果我注释掉试运行并执行命令,则操作确实会持续。我在试运行和实际运行前后检查了DB::transactionLevel(),一切似乎都是正确的。

我也尝试过使用DB::connection($connection)->pretend(...),但仍然是同样的问题。我还尝试在回滚后执行DB::purge($connection)DB::reconnect($connection)

有人对发生的事情有任何想法吗?

(使用 Laravel v6.20.14

【问题讨论】:

  • DB::connection($connection)->transaction(function() {}) 祝你好运
  • 那里也没有运气@ChinhNguyen
  • 你确定你的代码通过了dryRun检查
  • 我不确定你的意思@ChinhNguyen?我确实知道试运行会执行并向我显示差异。但是如果我选择实际删除,它不会真正被删除。如果我在没有空运行的情况下运行命令,它实际上会删除它。
  • 我还不知道原因,但我找到了解决您问题的方法。当调用 dryRun 时传递 $modelsToDelete 对象的深层副本,例如:$this->dryRun(unserialize(serialize($modelsToDelete)));。我猜是因为在调用 forceDelete() 之后,模型以后不会再查询 delete(只是猜测)

标签: mysql laravel transactions laravel-artisan


【解决方案1】:

在挖掘源代码后,我发现在模型实例上调用delete后,laravel set property "exists" 为false,它不会再执行delete查询。你可以参考:

https://github.com/laravel/framework/blob/9edd46fc6dcd550e4fd5d081bea37b0a43162165/src/Illuminate/Database/Eloquent/Model.php#L1173 https://github.com/laravel/framework/blob/9edd46fc6dcd550e4fd5d081bea37b0a43162165/src/Illuminate/Database/Eloquent/Model.php#L1129

为了让模型实例在dryRun之后可以被删除,你应该传递一个深拷贝给dryRun,例如:

$this->dryRun(unserialize(serialize($modelsToDelete)));

注意:不要使用php clone,因为它会创建一个浅拷贝

【讨论】:

    【解决方案2】:
    1. 打开 MySQL 的“常规日志”。
    2. 运行给您带来麻烦的实验。
    3. 关闭该日志。

    问题在日志中可能很明显;如果不向我们显示日志。

    【讨论】:

    • 我现在的注意力有点分散,我会尽快整理一些日志。感谢您的建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    相关资源
    最近更新 更多