swooletw / laravel-swoole

High performance HTTP server based on Swoole. Speed up your Laravel or Lumen applications.
MIT License
4.04k stars 389 forks source link

DB transaction not rollBack automatically after an exception. #218

Closed lokielse closed 5 years ago

lokielse commented 5 years ago
  1. Please provide your PHP and Swoole version. (php -v and php --ri swoole)

    PHP version : 7.2.6
    Swoole version : 4.2.6
  2. Please provide your Laravel/Lumen version.

    Laravel : v5.6.39
  3. Which release version of this package are you using?

    laravel-swoole : v2.6.2
  4. What did you do? If possible, provide a recipe for reproducing the error.

    Step.0: Preparation

Laravel config database.php

'mysql'            => [
'driver'    => 'mysql',
'host'      => env('DB_HOST', 'localhost'),
'database'  => env('DB_DATABASE'),
'username'  => env('DB_USERNAME'),
'password'  => env('DB_PASSWORD'),
'charset'   => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix'    => '',
'strict'    => false,
],

Laravel config swoole_http.php

'reactor_num'        => 1,
'worker_num'         => 1,
'task_worker_num'    => 1,
CREATE TABLE `swoole_dead_locks` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `swoole_orders` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `swoole_dead_locks` (`id`, `name`) VALUES (1, NULL);

Step.1: Simulate a session start a long time transaction

in phpmyadmin.

BEGIN;  /* Start a transaction */
SELECT * FROM `swoole_dead_locks` where id=1 FOR UPDATE;

Step.2: Simulate a session in a dead lock

GET /test/swoole/1


Route::get('test/swoole/1', function () {
DB::beginTransaction();
DB::table('swoole_dead_locks')->lock()->where('id', 1)->first();
// It will throw a dead lock exception after about 30 seconds.

// or simply
// sleep(5);
// throw new Exception();

// never reach here.
DB::commit();

return 'success';

});


## Step.3: Create an order
> GET /test/swoole/2
```php
Route::get('test/swoole/2', function () {
    // DB::beginTransaction(); //  use transaction or not dose not make sense

    DB::table('swoole_orders')->insert([ 'title' => 'foobar.' . now()->toDateTimeString() ]);
    $order = DB::table('swoole_orders')->latest('id')->first();

   // DB::commit(); //  use transaction or not dose not make sense

    return response()->json($order);
});

‼️ Step 3 always response an order to client in each request after a dead lock exception in step 2, while theses orders were not really commit into database.

  1. What did you expect to see?
  2. It's better to make transaction rollBack automatically after an exception which without any catch (use exceptions/Handler.php).
  3. Make some tips about this issue on the Wiki Notices page
  4. What did you see instead? ghost orders

196

albertcht commented 5 years ago

Thanks for your detailed steps, I will check it!

albertcht commented 5 years ago

Can you try to add db to instances in swoole_http.php?

lokielse commented 5 years ago

Thanks @albertcht

Adding db to instances in swoole_http.php raised an exception which located the bug.

I used laravel-transactional-events in my project, which cause the problem.

After i remove laravel-transactional-events as well as removeing db from instances. everything works like a charm.

Thank you again, laravel-swoole is an awesome project.

albertcht commented 5 years ago

anytime.