Skip to content

Commit 6a047d3

Browse files
committed
add persistence, sync, and forceRefresh assertions
1 parent fd830be commit 6a047d3

10 files changed

+562
-29
lines changed

README.md

+34-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,43 @@ In addition to the tests we can write using testbench, we have this repository w
44
1. Creates a new Laravel application
55
2. Sets up Tenancy
66
3. Creates a sample job
7-
4. Asserts that the queue worker is working as expected
7+
4. Asserts that the queue worker is working as expected -- running in the correct context and responding to restart signals
88

99
This is mostly due to some past bugs that were hard to catch in our test suite.
1010

1111
With this repo, we can have a separate CI job validating queue behavior _in a real application_.
1212

13-
## TODOs
13+
## Persistence tests
1414

15-
- Verify how `queue:restart` works in v4
15+
Additionally, we can also test for _queue worker persistence_. This refers to the worker staying in the context of the tenant
16+
used in the last job. The benefit of that is significantly better third-party package support (especially in cases where said
17+
packages unserialize job payloads on e.g. `JobProcessed`).
18+
19+
In versions prior to v4:
20+
- 3.8.5 handles restarts correctly but is not persistent
21+
- 3.8.4 is persistent but doesn't respond to restarts correctly (if the last processed job was in the tenant context)
22+
23+
In v4, there's `QueueTenancyBootstrapper` that works similarly to 3.8.5 and `PersistentQueueTenancyBootstrapper` that works
24+
similarly to 3.8.4.
25+
26+
For the different setups:
27+
- 3.x should have only warns on missing persistence
28+
- 3.8.4 fails the restart-related assertions. The alternative config (./alternative_config.sh) makes them pass.
29+
- 3.8.4 fails the FORCEREFRESH-related assertions. Either run with FORCEREFRESH=0 or set `QueueTenancyBootstrapper::$forceRefresh = true` in a service provider.
30+
- 4.x should only show warns on missing persistence
31+
- With the alternative config, it should pass ALL tests without any warnings.
32+
33+
3.x (3.8.5+) tests:
34+
```bash
35+
./setup.sh
36+
./test.sh
37+
```
38+
39+
4.x tests:
40+
```bash
41+
./setup.sh
42+
./test.sh
43+
44+
./alternative_config.sh
45+
PERSISTENT=1 ./test.sh
46+
```

alternative_config.sh

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
# Sets up the "alternative config" mentioned here https://github.com/archtechx/tenancy/issues/1260#issuecomment-2572951587
4+
5+
set -e
6+
7+
docker run --rm -v .:/var/www/html tenancy-queue-test-cli bash setup/alternative/_alternative_setup.sh

setup/AppServiceProvider.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace App\Providers;
4+
5+
use Illuminate\Queue\Events\JobProcessed;
6+
use Illuminate\Support\Facades\Event;
7+
use Illuminate\Support\ServiceProvider;
8+
9+
class AppServiceProvider extends ServiceProvider
10+
{
11+
/**
12+
* Register any application services.
13+
*/
14+
public function register(): void
15+
{
16+
//
17+
}
18+
19+
/**
20+
* Bootstrap any application services.
21+
*/
22+
public function boot(): void
23+
{
24+
Event::listen(JobProcessed::class, function () {
25+
file_put_contents(base_path('jobprocessed_context'), tenant() ? ('tenant_' . tenant('id')) : 'central');
26+
});
27+
}
28+
}

setup/LogAbcJob.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace App\Jobs;
4+
5+
use Illuminate\Bus\Queueable;
6+
use Illuminate\Foundation\Bus\Dispatchable;
7+
use Illuminate\Queue\InteractsWithQueue;
8+
use Illuminate\Queue\SerializesModels;
9+
use Illuminate\Contracts\Queue\ShouldQueue;
10+
11+
class LogAbcJob implements ShouldQueue
12+
{
13+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
14+
15+
public function handle(): void
16+
{
17+
file_put_contents(base_path('abc'), tenant('abc'));
18+
}
19+
}
20+

setup/_tenancy_setup.sh

+4
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,7 @@ cp database/migrations/*create_users*.php database/migrations/tenant
3939

4040
mkdir app/Jobs
4141
cp ../setup/FooJob.php app/Jobs/FooJob.php
42+
cp ../setup/LogAbcJob.php app/Jobs/LogAbcJob.php
43+
44+
rm app/Providers/AppServiceProvider.php
45+
cp ../setup/AppServiceProvider.php app/Providers/AppServiceProvider.php
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace App\Providers;
4+
5+
use Illuminate\Queue\Console\RestartCommand;
6+
use Illuminate\Queue\Console\WorkCommand;
7+
use Illuminate\Queue\Events\JobProcessed;
8+
use Illuminate\Support\Facades\Event;
9+
use Illuminate\Support\ServiceProvider;
10+
11+
class AppServiceProvider extends ServiceProvider
12+
{
13+
/**
14+
* Register any application services.
15+
*/
16+
public function register(): void
17+
{
18+
$this->app->extend(RestartCommand::class, function ($_, $app) {
19+
return new RestartCommand($app['cache']->store('global_redis'));
20+
});
21+
$this->app->extend(WorkCommand::class, function ($_, $app) {
22+
return new WorkCommand($app['queue.worker'], $app['cache']->store('global_redis'));
23+
});
24+
}
25+
26+
/**
27+
* Bootstrap any application services.
28+
*/
29+
public function boot(): void
30+
{
31+
Event::listen(JobProcessed::class, function () {
32+
file_put_contents(base_path('jobprocessed_context'), tenant() ? ('tenant_' . tenant('id')) : 'central');
33+
});
34+
}
35+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
cd src/
6+
7+
rm config/cache.php
8+
cp ../setup/alternative/cache.php config/cache.php
9+
10+
rm config/database.php
11+
cp ../setup/alternative/database.php config/database.php
12+
13+
rm app/Providers/AppServiceProvider.php
14+
cp ../setup/alternative/AppServiceProvider.php app/Providers/AppServiceProvider.php

setup/alternative/cache.php

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
use Illuminate\Support\Str;
4+
5+
return [
6+
7+
/*
8+
|--------------------------------------------------------------------------
9+
| Default Cache Store
10+
|--------------------------------------------------------------------------
11+
|
12+
| This option controls the default cache store that will be used by the
13+
| framework. This connection is utilized if another isn't explicitly
14+
| specified when running a cache operation inside the application.
15+
|
16+
*/
17+
18+
'default' => env('CACHE_STORE', 'database'),
19+
20+
/*
21+
|--------------------------------------------------------------------------
22+
| Cache Stores
23+
|--------------------------------------------------------------------------
24+
|
25+
| Here you may define all of the cache "stores" for your application as
26+
| well as their drivers. You may even define multiple stores for the
27+
| same cache driver to group types of items stored in your caches.
28+
|
29+
| Supported drivers: "array", "database", "file", "memcached",
30+
| "redis", "dynamodb", "octane", "null"
31+
|
32+
*/
33+
34+
'stores' => [
35+
36+
'array' => [
37+
'driver' => 'array',
38+
'serialize' => false,
39+
],
40+
41+
'database' => [
42+
'driver' => 'database',
43+
'connection' => env('DB_CACHE_CONNECTION'),
44+
'table' => env('DB_CACHE_TABLE', 'cache'),
45+
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
46+
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
47+
],
48+
49+
'file' => [
50+
'driver' => 'file',
51+
'path' => storage_path('framework/cache/data'),
52+
'lock_path' => storage_path('framework/cache/data'),
53+
],
54+
55+
'memcached' => [
56+
'driver' => 'memcached',
57+
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
58+
'sasl' => [
59+
env('MEMCACHED_USERNAME'),
60+
env('MEMCACHED_PASSWORD'),
61+
],
62+
'options' => [
63+
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
64+
],
65+
'servers' => [
66+
[
67+
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
68+
'port' => env('MEMCACHED_PORT', 11211),
69+
'weight' => 100,
70+
],
71+
],
72+
],
73+
74+
'redis' => [
75+
'driver' => 'redis',
76+
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
77+
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
78+
],
79+
80+
'global_redis' => [
81+
'driver' => 'redis',
82+
'connection' => 'global_cache',
83+
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
84+
],
85+
86+
'dynamodb' => [
87+
'driver' => 'dynamodb',
88+
'key' => env('AWS_ACCESS_KEY_ID'),
89+
'secret' => env('AWS_SECRET_ACCESS_KEY'),
90+
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
91+
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
92+
'endpoint' => env('DYNAMODB_ENDPOINT'),
93+
],
94+
95+
'octane' => [
96+
'driver' => 'octane',
97+
],
98+
99+
],
100+
101+
/*
102+
|--------------------------------------------------------------------------
103+
| Cache Key Prefix
104+
|--------------------------------------------------------------------------
105+
|
106+
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
107+
| stores, there might be other applications using the same cache. For
108+
| that reason, you may prefix every cache key to avoid collisions.
109+
|
110+
*/
111+
112+
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
113+
114+
];

0 commit comments

Comments
 (0)