From d9c06211d32307142f3c7dfe476d7006ed8f4c2b Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 19 Sep 2024 14:50:58 +0200 Subject: [PATCH 1/7] update tests for SQLSRV --- .../Constraints/SeeInDatabaseExtended.php | 48 +++++++++++++++++++ tests/_support/TestCase.php | 19 ++++++++ 2 files changed, 67 insertions(+) create mode 100644 tests/_support/Constraints/SeeInDatabaseExtended.php diff --git a/tests/_support/Constraints/SeeInDatabaseExtended.php b/tests/_support/Constraints/SeeInDatabaseExtended.php new file mode 100644 index 0000000..96397da --- /dev/null +++ b/tests/_support/Constraints/SeeInDatabaseExtended.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Constraints; + +use CodeIgniter\Test\Constraints\SeeInDatabase; + +class SeeInDatabaseExtended extends SeeInDatabase +{ + /** + * Gets a string representation of the constraint + * + * @param int $options + */ + public function toString(bool $exportObjects = false, $options = 0): string + { + $this->data = array_combine( + array_map(fn ($key) => $this->extractFieldName($key), array_keys($this->data)), + $this->data + ); + + return parent::toString($exportObjects, $options); + } + + /** + * Extract field name from complex key + */ + protected function extractFieldName(string $input): string + { + $pattern = '/CONVERT\(\s*\w+,\s*(\w+)\s*\)/'; + + if (preg_match($pattern, $input, $matches)) { + return $matches[1]; + } + + return $input; + } +} diff --git a/tests/_support/TestCase.php b/tests/_support/TestCase.php index 0b956db..2718d9d 100644 --- a/tests/_support/TestCase.php +++ b/tests/_support/TestCase.php @@ -17,6 +17,7 @@ use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use Exception; +use Tests\Support\Constraints\SeeInDatabaseExtended; abstract class TestCase extends CIUnitTestCase { @@ -41,4 +42,22 @@ protected function tearDown(): void // Reset the current time. Time::setTestNow(); } + + public function seeInDatabaseExtended(string $table, array $where): void + { + $constraint = new SeeInDatabaseExtended($this->db, $where); + $this->assertThat($table, $constraint); + } + + /** + * Handle custom field type conversion for SQLSRV + */ + public function field(string $name): string + { + if ($this->db->DBDriver === 'SQLSRV') { + return "CONVERT(VARCHAR, {$name})"; + } + + return $name; + } } From 6d46607ddeb15223927a3d82328489f909bffc0b Mon Sep 17 00:00:00 2001 From: michalsn Date: Thu, 19 Sep 2024 18:19:24 +0200 Subject: [PATCH 2/7] fix sqlsrv tests --- tests/_support/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/_support/TestCase.php b/tests/_support/TestCase.php index 2718d9d..e6d96c1 100644 --- a/tests/_support/TestCase.php +++ b/tests/_support/TestCase.php @@ -55,7 +55,7 @@ public function seeInDatabaseExtended(string $table, array $where): void public function field(string $name): string { if ($this->db->DBDriver === 'SQLSRV') { - return "CONVERT(VARCHAR, {$name})"; + return "CONVERT(VARCHAR(MAX), {$name})"; } return $name; From 99a3cd9c2b3a5328a163ed8452487c922ab5d55f Mon Sep 17 00:00:00 2001 From: michalsn Date: Fri, 27 Dec 2024 11:52:07 +0100 Subject: [PATCH 3/7] simplify the code as text type is deprecated in SQLSRV --- .../Constraints/SeeInDatabaseExtended.php | 48 ------------------- tests/_support/TestCase.php | 7 --- 2 files changed, 55 deletions(-) delete mode 100644 tests/_support/Constraints/SeeInDatabaseExtended.php diff --git a/tests/_support/Constraints/SeeInDatabaseExtended.php b/tests/_support/Constraints/SeeInDatabaseExtended.php deleted file mode 100644 index 96397da..0000000 --- a/tests/_support/Constraints/SeeInDatabaseExtended.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Tests\Support\Constraints; - -use CodeIgniter\Test\Constraints\SeeInDatabase; - -class SeeInDatabaseExtended extends SeeInDatabase -{ - /** - * Gets a string representation of the constraint - * - * @param int $options - */ - public function toString(bool $exportObjects = false, $options = 0): string - { - $this->data = array_combine( - array_map(fn ($key) => $this->extractFieldName($key), array_keys($this->data)), - $this->data - ); - - return parent::toString($exportObjects, $options); - } - - /** - * Extract field name from complex key - */ - protected function extractFieldName(string $input): string - { - $pattern = '/CONVERT\(\s*\w+,\s*(\w+)\s*\)/'; - - if (preg_match($pattern, $input, $matches)) { - return $matches[1]; - } - - return $input; - } -} diff --git a/tests/_support/TestCase.php b/tests/_support/TestCase.php index e6d96c1..4e8009a 100644 --- a/tests/_support/TestCase.php +++ b/tests/_support/TestCase.php @@ -17,7 +17,6 @@ use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use Exception; -use Tests\Support\Constraints\SeeInDatabaseExtended; abstract class TestCase extends CIUnitTestCase { @@ -43,12 +42,6 @@ protected function tearDown(): void Time::setTestNow(); } - public function seeInDatabaseExtended(string $table, array $where): void - { - $constraint = new SeeInDatabaseExtended($this->db, $where); - $this->assertThat($table, $constraint); - } - /** * Handle custom field type conversion for SQLSRV */ From 834079ab6f1bff352f7f8a0da7776070554b9eb2 Mon Sep 17 00:00:00 2001 From: michalsn Date: Fri, 27 Dec 2024 12:12:18 +0100 Subject: [PATCH 4/7] update sqlsrv extension in the workflow --- .github/workflows/phpunit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index a63634b..cd5209a 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -146,7 +146,7 @@ jobs: with: php-version: ${{ matrix.php-versions }} tools: composer, phive, phpunit - extensions: intl, json, mbstring, gd, xdebug, xml, sqlite3, sqlsrv, oci8, pgsql + extensions: intl, json, mbstring, gd, xdebug, xml, sqlite3, sqlsrv-5.10.1, oci8, pgsql coverage: xdebug env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 15d342a1937b6c620470d874844a032662fd31ec Mon Sep 17 00:00:00 2001 From: michalsn Date: Fri, 27 Dec 2024 12:24:07 +0100 Subject: [PATCH 5/7] cleanup --- .github/workflows/phpunit.yml | 2 +- tests/_support/TestCase.php | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index cd5209a..a63634b 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -146,7 +146,7 @@ jobs: with: php-version: ${{ matrix.php-versions }} tools: composer, phive, phpunit - extensions: intl, json, mbstring, gd, xdebug, xml, sqlite3, sqlsrv-5.10.1, oci8, pgsql + extensions: intl, json, mbstring, gd, xdebug, xml, sqlite3, sqlsrv, oci8, pgsql coverage: xdebug env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/tests/_support/TestCase.php b/tests/_support/TestCase.php index 4e8009a..0b956db 100644 --- a/tests/_support/TestCase.php +++ b/tests/_support/TestCase.php @@ -41,16 +41,4 @@ protected function tearDown(): void // Reset the current time. Time::setTestNow(); } - - /** - * Handle custom field type conversion for SQLSRV - */ - public function field(string $name): string - { - if ($this->db->DBDriver === 'SQLSRV') { - return "CONVERT(VARCHAR(MAX), {$name})"; - } - - return $name; - } } From 2fbd94efbb3c7c173ef40f9cca507b5e5cd58e4b Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 23 Sep 2024 08:42:59 +0200 Subject: [PATCH 6/7] disable strict mode for transactions --- docs/basic-usage.md | 46 ++++++++++++++++++++++++++++++ docs/configuration.md | 4 +++ src/Models/QueueJobFailedModel.php | 19 ++++++++++++ src/Models/QueueJobModel.php | 19 ++++++++++++ 4 files changed, 88 insertions(+) diff --git a/docs/basic-usage.md b/docs/basic-usage.md index afe403f..e7c2aa6 100644 --- a/docs/basic-usage.md +++ b/docs/basic-usage.md @@ -79,6 +79,52 @@ You may be wondering what the `$this->data['message']` variable is all about. We Throwing an exception is a way to let the queue worker know that the job has failed. +#### Using transactions + +If you have to use transactions in our Jobs - this is a simple schema you can follow. + +!!! note + + Due to the nature of the queue worker, [Strict Mode](https://codeigniter.com/user_guide/database/transactions.html#strict-mode) is automatically disabled for the database connection assigned to the Database handler. + + If you use the same connection group in your Jobs as defined in the Database handler, then in that case, you don't need to do anything. + + On the other hand, if you are using a different group to connect to the database in your Jobs, then if you are using transactions, you should disable Strict Mode through the method: `$db->transStrict(false)` or by setting the `transStrict` option to `false` in your connection config group - the last option will disable Strict Mode globally. + +```php +// ... + +class Email extends BaseJob implements JobInterface +{ + /** + * @throws Exception + */ + public function process(string $data): + { + try { + $db = db_connect(); + // Disable Strict Mode + $db->transStrict(false); + $db->transBegin(); + + // Job logic goes here + // Your code should throw an exception on error + + if ($db->transStatus() === false) { + $db->transRollback(); + } else { + $db->transCommit(); + } + } catch (Exception $e) { + $db->transRollback(); + throw $e; + } + } +} +``` + +#### Other options + We can also configure some things on the job level. It's a number of tries, when the job is failing and time after the job will be retried again after failure. We can specify these options by using variables: ```php diff --git a/docs/configuration.md b/docs/configuration.md index a710146..c26eb5c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -39,6 +39,10 @@ The configuration settings for `database` handler. * `getShared` - Weather to use shared instance. Default value: `true`. * `skipLocked` - Weather to use "skip locked" feature to maintain concurrency calls. Default to `true`. +!!! note + + The [Strict Mode](https://codeigniter.com/user_guide/database/transactions.html#strict-mode) for the given `dbGroup` is automatically disabled - due to the nature of the queue worker. + ### $redis The configuration settings for `redis` handler. You need to have a [ext-redis](https://github.com/phpredis/phpredis) installed to use it. diff --git a/src/Models/QueueJobFailedModel.php b/src/Models/QueueJobFailedModel.php index 0574da6..e98b5f3 100644 --- a/src/Models/QueueJobFailedModel.php +++ b/src/Models/QueueJobFailedModel.php @@ -13,8 +13,12 @@ namespace CodeIgniter\Queue\Models; +use CodeIgniter\Database\BaseConnection; +use CodeIgniter\Database\ConnectionInterface; use CodeIgniter\Model; use CodeIgniter\Queue\Entities\QueueJobFailed; +use CodeIgniter\Validation\ValidationInterface; +use Config\Database; class QueueJobFailedModel extends Model { @@ -37,4 +41,19 @@ class QueueJobFailedModel extends Model // Callbacks protected $allowCallbacks = false; + + public function __construct(?ConnectionInterface $db = null, ?ValidationInterface $validation = null) + { + $this->DBGroup = config('Queue')->database['dbGroup']; + + /** + * @var BaseConnection|null $db + */ + $db ??= Database::connect($this->DBGroup); + + // Turn off the Strict Mode + $db->transStrict(false); + + parent::__construct($db, $validation); + } } diff --git a/src/Models/QueueJobModel.php b/src/Models/QueueJobModel.php index ed7d26f..6aaf910 100644 --- a/src/Models/QueueJobModel.php +++ b/src/Models/QueueJobModel.php @@ -14,10 +14,14 @@ namespace CodeIgniter\Queue\Models; use CodeIgniter\Database\BaseBuilder; +use CodeIgniter\Database\BaseConnection; +use CodeIgniter\Database\ConnectionInterface; use CodeIgniter\I18n\Time; use CodeIgniter\Model; use CodeIgniter\Queue\Entities\QueueJob; use CodeIgniter\Queue\Enums\Status; +use CodeIgniter\Validation\ValidationInterface; +use Config\Database; use ReflectionException; class QueueJobModel extends Model @@ -42,6 +46,21 @@ class QueueJobModel extends Model // Callbacks protected $allowCallbacks = false; + public function __construct(?ConnectionInterface $db = null, ?ValidationInterface $validation = null) + { + $this->DBGroup = config('Queue')->database['dbGroup']; + + /** + * @var BaseConnection|null $db + */ + $db ??= Database::connect($this->DBGroup); + + // Turn off the Strict Mode + $db->transStrict(false); + + parent::__construct($db, $validation); + } + /** * Get the oldest item from the queue. * From 985972c9d95d7a8004bc763deb169ab7904afb35 Mon Sep 17 00:00:00 2001 From: michalsn Date: Mon, 30 Dec 2024 21:54:19 +0100 Subject: [PATCH 7/7] update docs --- docs/basic-usage.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/basic-usage.md b/docs/basic-usage.md index e7c2aa6..5622a82 100644 --- a/docs/basic-usage.md +++ b/docs/basic-usage.md @@ -81,15 +81,15 @@ Throwing an exception is a way to let the queue worker know that the job has fai #### Using transactions -If you have to use transactions in our Jobs - this is a simple schema you can follow. +If you have to use transactions in your Job - this is a simple schema you can follow. !!! note - Due to the nature of the queue worker, [Strict Mode](https://codeigniter.com/user_guide/database/transactions.html#strict-mode) is automatically disabled for the database connection assigned to the Database handler. + Due to the nature of the queue worker, [Strict Mode](https://codeigniter.com/user_guide/database/transactions.html#strict-mode) is automatically disabled for the database connection assigned to the Database handler. That's because queue worker is a long-running process, and we don't want one failed transaction to affect others. - If you use the same connection group in your Jobs as defined in the Database handler, then in that case, you don't need to do anything. + If you use the same connection group in your Job as defined in the Database handler, then in that case, you don't need to do anything. - On the other hand, if you are using a different group to connect to the database in your Jobs, then if you are using transactions, you should disable Strict Mode through the method: `$db->transStrict(false)` or by setting the `transStrict` option to `false` in your connection config group - the last option will disable Strict Mode globally. + On the other hand, if you are using a different group to connect to the database in your Job, then if you are using transactions, you should disable Strict Mode through the method: `$db->transStrict(false)` or by setting the `transStrict` option to `false` in your connection config group - the last option will disable Strict Mode globally. ```php // ...