Skip to content

Commit 83948f0

Browse files
committed
Refactor for a single horizon instance to improve memory
1 parent fe817e6 commit 83948f0

5 files changed

Lines changed: 100 additions & 95 deletions

File tree

ProcessMaker/Multitenancy/MakeQueueTenantAwareAction.php

Lines changed: 0 additions & 27 deletions
This file was deleted.

ProcessMaker/Multitenancy/TenantAwareDispatcher.php

Lines changed: 0 additions & 31 deletions
This file was deleted.

ProcessMaker/Multitenancy/TenantBootstrapper.php

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,33 @@
1717
*/
1818
class TenantBootstrapper
1919
{
20+
private static $landlordValues = [];
21+
2022
private $encrypter = null;
2123

2224
private $pdo = null;
2325

24-
private $originalValues = null;
26+
private $app = null;
27+
28+
public static $landlordKeysToSave = [
29+
'APP_URL',
30+
'APP_KEY',
31+
'LOG_PATH',
32+
'DB_USERNAME',
33+
'DB_PASSWORD',
34+
'REDIS_PREFIX',
35+
'CACHE_SETTING_PREFIX',
36+
'SCRIPT_MICROSERVICE_CALLBACK',
37+
];
2538

2639
public function bootstrap(Application $app)
2740
{
2841
if (!$this->env('MULTITENANCY')) {
2942
return;
3043
}
44+
$this->app = $app;
3145

32-
// We need to save the original values for running horizon
33-
$this->saveOriginalValues();
46+
self::saveLandlordValues($app);
3447

3548
$tenantData = null;
3649

@@ -49,29 +62,30 @@ public function bootstrap(Application $app)
4962

5063
return;
5164
}
52-
$this->setTenantEnvironmentVariables($app, $tenantData);
65+
$this->setTenantEnvironmentVariables($tenantData);
5366

5467
// Use tenant's translation files. Doing this here so it's available in cached filesystems.php
5568
$app->useLangPath(resource_path('lang/tenant_' . $tenantData['id']));
5669

57-
$tenantData['original_values'] = $this->getOriginalValue();
70+
$tenantData['original_values'] = self::$landlordValues;
5871
Tenant::setBootstrappedTenant($app, $tenantData);
5972
}
6073

61-
private function setTenantEnvironmentVariables($app, $tenantData)
74+
private function setTenantEnvironmentVariables($tenantData)
6275
{
6376
// Additional configs are set in SwitchTenant.php
6477

6578
$tenantId = $tenantData['id'];
6679
$config = json_decode($tenantData['config'], true);
6780

68-
$this->set('APP_CONFIG_CACHE', $app->basePath('storage/tenant_' . $tenantId . '/config.php'));
81+
$this->set('APP_CONFIG_CACHE', $this->app->basePath('storage/tenant_' . $tenantId . '/config.php'));
6982
// Do not override packages cache path for now. Wait until the License service is updated.
70-
// $this->set('APP_PACKAGES_CACHE', $app->basePath('storage/tenant_' . $tenantId . '/packages.php'));
71-
$this->set('LARAVEL_STORAGE_PATH', $app->basePath('storage/tenant_' . $tenantId));
83+
// $this->set('APP_PACKAGES_CACHE', $this->app->basePath('storage/tenant_' . $tenantId . '/packages.php'));
84+
$this->set('LARAVEL_STORAGE_PATH', $this->app->basePath('storage/tenant_' . $tenantId));
7285
$this->set('APP_URL', $config['app.url']);
7386
$this->set('APP_KEY', $this->decrypt($config['app.key']));
74-
$this->set('DB_DATABASE', $tenantData['database']);
87+
$value = $tenantData['database'];
88+
$this->set('DB_DATABASE', $value);
7589
$this->set('DB_USERNAME', $tenantData['username'] ?? $this->getOriginalValue('DB_USERNAME'));
7690

7791
$encryptedPassword = $tenantData['password'];
@@ -83,51 +97,45 @@ private function setTenantEnvironmentVariables($app, $tenantData)
8397
}
8498

8599
$this->set('DB_PASSWORD', $password);
86-
$this->set('REDIS_PREFIX', $this->getOriginalValue('REDIS_PREFIX') . 'tenant-' . $tenantId . ':');
87-
$this->set('LOG_PATH', $app->basePath('storage/tenant_' . $tenantId . '/logs/processmaker.log'));
100+
// Commenting this out fixes the redis queue, but what about cache????
101+
// $this->set('REDIS_PREFIX', $this->getOriginalValue('REDIS_PREFIX') . 'tenant-' . $tenantId . ':');
102+
// $this->debug("Setting log path to " . $this->app->basePath('storage/tenant_' . $tenantId . '/logs/processmaker.log'));
103+
$this->set('LOG_PATH', $this->app->basePath('storage/tenant_' . $tenantId . '/logs/processmaker.log'));
88104
}
89105

90-
private function saveOriginalValues()
106+
public static function saveLandlordValues($app)
91107
{
92-
if ($this->env('ORIGINAL_VALUES')) {
108+
if ($app->has('landlordValues')) {
109+
self::$landlordValues = $app->make('landlordValues');
110+
93111
return;
94112
}
95-
$toSave = [
96-
'APP_URL',
97-
'APP_KEY',
98-
'DB_USERNAME',
99-
'DB_PASSWORD',
100-
'REDIS_PREFIX',
101-
'CACHE_SETTING_PREFIX',
102-
'SCRIPT_MICROSERVICE_CALLBACK',
103-
];
104-
$values = [];
105-
foreach ($toSave as $key) {
106-
$values[$key] = $this->env($key);
113+
114+
foreach (self::$landlordKeysToSave as $key) {
115+
self::$landlordValues[$key] = $_SERVER[$key] ?? '';
107116
}
108-
$this->set('ORIGINAL_VALUES', serialize($values));
109117
}
110118

111-
private function getOriginalValue($key = null)
119+
private function getOriginalValue($key)
112120
{
113-
if (!$this->originalValues) {
114-
$this->originalValues = unserialize($this->env('ORIGINAL_VALUES'));
115-
}
116-
if (!$key) {
117-
return $this->originalValues;
121+
if (!isset(self::$landlordValues[$key])) {
122+
// throw new \Exception('Landlord value not found in `landlordValues`: ' . $key);
123+
return '';
118124
}
119125

120-
return $this->originalValues[$key];
126+
return self::$landlordValues[$key];
121127
}
122128

123129
private function env($key, $default = null)
124130
{
125-
return Env::get($key, $default);
131+
return $_SERVER[$key] ?? $default;
126132
}
127133

128134
private function set($key, $value)
129135
{
130-
Env::getRepository()->set($key, $value);
136+
// Env::getRepository() is immutable but will use values from $_SERVER and $_ENV
137+
$_SERVER[$key] = $value;
138+
$_ENV[$key] = $value;
131139
}
132140

133141
private function decrypt($value)
@@ -174,4 +182,9 @@ private function executeQuery($query, $params = [])
174182

175183
return $stmt->fetch();
176184
}
185+
186+
private function debug($message)
187+
{
188+
file_put_contents(base_path('storage/debug.log'), $message . PHP_EOL, FILE_APPEND);
189+
}
177190
}

ProcessMaker/Providers/ProcessMakerServiceProvider.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
1010
use Illuminate\Notifications\Events\BroadcastNotificationCreated;
1111
use Illuminate\Notifications\Events\NotificationSent;
12+
use Illuminate\Queue\Events\JobAttempted;
13+
use Illuminate\Queue\Events\JobProcessing;
1214
use Illuminate\Support\Arr;
1315
use Illuminate\Support\Env;
1416
use Illuminate\Support\Facades;
17+
use Illuminate\Support\Facades\Context;
1518
use Illuminate\Support\Facades\DB;
1619
use Illuminate\Support\Facades\Log;
1720
use Illuminate\Support\Facades\Route;
@@ -37,6 +40,7 @@
3740
use ProcessMaker\Managers\ScreenCompiledManager;
3841
use ProcessMaker\Models;
3942
use ProcessMaker\Multitenancy\Tenant;
43+
use ProcessMaker\Multitenancy\TenantBootstrapper;
4044
use ProcessMaker\Observers;
4145
use ProcessMaker\PolicyExtension;
4246
use ProcessMaker\Repositories\SettingsConfigRepository;
@@ -62,6 +66,9 @@ class ProcessMakerServiceProvider extends ServiceProvider
6266
// Track the query time for each request
6367
private static $queryTime = 0;
6468

69+
// Track the landlord values for multitenancy
70+
private static $landlordValues = null;
71+
6572
public function boot(): void
6673
{
6774
// Track the start time for service providers boot
@@ -233,6 +240,50 @@ public function register(): void
233240
*/
234241
protected static function registerEvents(): void
235242
{
243+
Facades\Event::listen(JobProcessing::class, function ($event) {
244+
Context::hydrate($event->job->payload()['illuminate:log:context'] ?? null);
245+
$tenantId = Context::get(config('multitenancy.current_tenant_context_key'));
246+
if ($tenantId) {
247+
if (!method_exists($event->job, 'getRedisQueue')) {
248+
// Not a redis job
249+
return;
250+
}
251+
252+
// Save the landlord's config values so we can reset them later
253+
if (self::$landlordValues === null) {
254+
foreach (TenantBootstrapper::$landlordKeysToSave as $key) {
255+
self::$landlordValues[$key] = $_SERVER[$key] ?? '';
256+
}
257+
}
258+
259+
// Create a new tenant app instance
260+
$_SERVER['TENANT'] = $tenantId;
261+
$_ENV['TENANT'] = $tenantId;
262+
$tenantApp = require app()->bootstrapPath('app.php');
263+
$tenantApp->instance('landlordValues', self::$landlordValues);
264+
$tenantApp->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
265+
266+
// Change the job's app service container to the tenant app
267+
$event->job->getRedisQueue()->setContainer($tenantApp);
268+
}
269+
});
270+
271+
Facades\Event::listen(JobAttempted::class, function ($event) {
272+
if (!method_exists($event->job, 'getRedisQueue')) {
273+
// Not a redis job
274+
return;
275+
}
276+
277+
unset($_SERVER['TENANT']);
278+
unset($_ENV['TENANT']);
279+
280+
// Restore the original values since the tenant boostrapper modified them
281+
foreach (self::$landlordValues as $key => $value) {
282+
$_SERVER[$key] = $value;
283+
$_ENV[$key] = $value;
284+
}
285+
});
286+
236287
// Listen to the events for our core screen
237288
// types and add our javascript
238289
Facades\Event::listen(ScreenBuilderStarting::class, function ($event) {

config/multitenancy.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@
8787
'actions' => [
8888
'make_tenant_current_action' => MakeTenantCurrentAction::class,
8989
'forget_current_tenant_action' => ForgetCurrentTenantAction::class,
90-
'make_queue_tenant_aware_action' => ProcessMaker\Multitenancy\MakeQueueTenantAwareAction::class,
9190
'migrate_tenant' => MigrateTenantAction::class,
9291
],
9392

0 commit comments

Comments
 (0)