perf(horizon): skip queues for platforms that are disabled (#321)
* perf(horizon): skip queues for platforms that are disabled
The social-publishing supervisor listens on Platform::allQueues(), which maps
over every enum case regardless of the per-platform *_ENABLED toggles. With
minProcesses => 1 that means one worker per supported platform - even on an
installation that only ever connects two or three of them.
On a single-workspace self-host that was 19 Horizon workers at roughly 75 MB
each; filtering by the toggles brought it to 9 and cut container memory from
1.66 GB to 1.06 GB, with no change in publishing behaviour.
Note on the implementation: the filter reads env() directly rather than calling
Platform::isEnabled(). Config files load alphabetically, so config('trypost.*')
does not exist yet while horizon.php is evaluated - isEnabled() would silently
return its default of true and the filter would be a no-op. This bites at
config:cache time, so it is invisible in tinker.
* refactor(horizon): filter disabled platform queues via enum
Move queue filtering to Platform::enabledQueues() and apply it from
AppServiceProvider after config has loaded, avoiding env() parsing in
horizon.php while keeping isEnabled() as the single source of truth.
* refactor(horizon): use enabledQueues directly in horizon config
Remove AppServiceProvider boot override and let isEnabled() fall back
to env when trypost config is not loaded yet.
* chore: enable Eloquent strict mode in all environments
* refactor(platform): replace enabled env key derivation with explicit match
* refactor(platform): simplify isEnabled using config default fallback
* test(platform): cover publishing queues and enabled toggles exhaustively
* refactor(platform): collapse isEnabled env fallback into one method
* refactor(platform): drop filter_var and rely on env boolean casting
* revert: keep Eloquent strict mode out of production
shouldBeStrict() in production would throw on lazy loads in queued
publish jobs and can stop posting. Restore the Laravel default.
---------
Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
This commit is contained in:
parent
abe687d0fa
commit
ca497e1f3c
3 changed files with 214 additions and 8 deletions
|
|
@ -380,6 +380,18 @@ public static function allQueues(): array
|
|||
return array_map(fn (self $platform) => $platform->queue(), self::cases());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function enabledQueues(): array
|
||||
{
|
||||
return collect(self::cases())
|
||||
->filter(fn (self $platform): bool => $platform->isEnabled())
|
||||
->map(fn (self $platform): string => $platform->queue())
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function instagramGraphBaseUrl(): string
|
||||
{
|
||||
return match ($this) {
|
||||
|
|
@ -390,7 +402,25 @@ public function instagramGraphBaseUrl(): string
|
|||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return config("trypost.platforms.{$this->value}.enabled", true);
|
||||
return (bool) config(
|
||||
"trypost.platforms.{$this->value}.enabled",
|
||||
env(match ($this) {
|
||||
self::LinkedIn => 'LINKEDIN_ENABLED',
|
||||
self::LinkedInPage => 'LINKEDIN_PAGE_ENABLED',
|
||||
self::X => 'X_ENABLED',
|
||||
self::TikTok => 'TIKTOK_ENABLED',
|
||||
self::YouTube => 'YOUTUBE_ENABLED',
|
||||
self::Facebook => 'FACEBOOK_ENABLED',
|
||||
self::Instagram => 'INSTAGRAM_ENABLED',
|
||||
self::InstagramFacebook => 'INSTAGRAM_FACEBOOK_ENABLED',
|
||||
self::Threads => 'THREADS_ENABLED',
|
||||
self::Pinterest => 'PINTEREST_ENABLED',
|
||||
self::Bluesky => 'BLUESKY_ENABLED',
|
||||
self::Mastodon => 'MASTODON_ENABLED',
|
||||
self::Telegram => 'TELEGRAM_ENABLED',
|
||||
self::Discord => 'DISCORD_ENABLED',
|
||||
}, true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@
|
|||
|
||||
'social-publishing' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => Platform::allQueues(),
|
||||
'queue' => Platform::enabledQueues(),
|
||||
'balance' => 'auto',
|
||||
'autoScalingStrategy' => 'time',
|
||||
'minProcesses' => 1,
|
||||
|
|
|
|||
|
|
@ -103,17 +103,193 @@
|
|||
expect(Platform::Facebook->defaultTokenTtlSeconds())->toBeNull();
|
||||
});
|
||||
|
||||
test('platform is enabled by default', function () {
|
||||
expect(Platform::LinkedIn->isEnabled())->toBeTrue();
|
||||
expect(Platform::Instagram->isEnabled())->toBeTrue();
|
||||
test('platform is enabled by default for every platform', function (Platform $platform) {
|
||||
expect($platform->isEnabled())->toBeTrue();
|
||||
})->with([
|
||||
Platform::LinkedIn,
|
||||
Platform::LinkedInPage,
|
||||
Platform::X,
|
||||
Platform::TikTok,
|
||||
Platform::YouTube,
|
||||
Platform::Facebook,
|
||||
Platform::Instagram,
|
||||
Platform::InstagramFacebook,
|
||||
Platform::Threads,
|
||||
Platform::Pinterest,
|
||||
Platform::Bluesky,
|
||||
Platform::Mastodon,
|
||||
Platform::Telegram,
|
||||
Platform::Discord,
|
||||
]);
|
||||
|
||||
test('each platform can be disabled via config', function (Platform $platform) {
|
||||
config(["trypost.platforms.{$platform->value}.enabled" => false]);
|
||||
|
||||
expect($platform->isEnabled())->toBeFalse();
|
||||
})->with([
|
||||
Platform::LinkedIn,
|
||||
Platform::LinkedInPage,
|
||||
Platform::X,
|
||||
Platform::TikTok,
|
||||
Platform::YouTube,
|
||||
Platform::Facebook,
|
||||
Platform::Instagram,
|
||||
Platform::InstagramFacebook,
|
||||
Platform::Threads,
|
||||
Platform::Pinterest,
|
||||
Platform::Bluesky,
|
||||
Platform::Mastodon,
|
||||
Platform::Telegram,
|
||||
Platform::Discord,
|
||||
]);
|
||||
|
||||
test('each platform maps to its publishing queue', function (Platform $platform, string $queue) {
|
||||
expect($platform->queue())->toBe($queue);
|
||||
})->with([
|
||||
[Platform::LinkedIn, 'social-linkedin'],
|
||||
[Platform::LinkedInPage, 'social-linkedin-page'],
|
||||
[Platform::X, 'social-x'],
|
||||
[Platform::TikTok, 'social-tiktok'],
|
||||
[Platform::YouTube, 'social-youtube'],
|
||||
[Platform::Facebook, 'social-facebook'],
|
||||
[Platform::Instagram, 'social-instagram'],
|
||||
[Platform::InstagramFacebook, 'social-instagram-facebook'],
|
||||
[Platform::Threads, 'social-threads'],
|
||||
[Platform::Pinterest, 'social-pinterest'],
|
||||
[Platform::Bluesky, 'social-bluesky'],
|
||||
[Platform::Mastodon, 'social-mastodon'],
|
||||
[Platform::Telegram, 'social-telegram'],
|
||||
[Platform::Discord, 'social-discord'],
|
||||
]);
|
||||
|
||||
test('allQueues lists every platform publishing queue in enum order', function () {
|
||||
expect(Platform::allQueues())->toBe([
|
||||
'social-linkedin',
|
||||
'social-linkedin-page',
|
||||
'social-x',
|
||||
'social-tiktok',
|
||||
'social-youtube',
|
||||
'social-facebook',
|
||||
'social-instagram',
|
||||
'social-instagram-facebook',
|
||||
'social-threads',
|
||||
'social-pinterest',
|
||||
'social-bluesky',
|
||||
'social-mastodon',
|
||||
'social-telegram',
|
||||
'social-discord',
|
||||
])->and(Platform::allQueues())->toHaveCount(count(Platform::cases()));
|
||||
});
|
||||
|
||||
test('platform can be disabled via config', function () {
|
||||
config(['trypost.platforms.linkedin.enabled' => false]);
|
||||
test('enabledQueues matches allQueues when every platform is enabled', function () {
|
||||
foreach (Platform::cases() as $platform) {
|
||||
config(["trypost.platforms.{$platform->value}.enabled" => true]);
|
||||
}
|
||||
|
||||
expect(Platform::LinkedIn->isEnabled())->toBeFalse();
|
||||
expect(Platform::enabledQueues())->toBe(Platform::allQueues());
|
||||
});
|
||||
|
||||
test('disabling a platform removes only its queue from enabledQueues', function (Platform $disabled) {
|
||||
foreach (Platform::cases() as $platform) {
|
||||
config(["trypost.platforms.{$platform->value}.enabled" => true]);
|
||||
}
|
||||
|
||||
config(["trypost.platforms.{$disabled->value}.enabled" => false]);
|
||||
|
||||
$enabledQueues = Platform::enabledQueues();
|
||||
|
||||
expect($enabledQueues)
|
||||
->not->toContain($disabled->queue())
|
||||
->toHaveCount(count(Platform::cases()) - 1);
|
||||
|
||||
foreach (Platform::cases() as $platform) {
|
||||
if ($platform === $disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
expect($enabledQueues)->toContain($platform->queue());
|
||||
}
|
||||
|
||||
expect(Platform::allQueues())->toContain($disabled->queue());
|
||||
})->with([
|
||||
Platform::LinkedIn,
|
||||
Platform::LinkedInPage,
|
||||
Platform::X,
|
||||
Platform::TikTok,
|
||||
Platform::YouTube,
|
||||
Platform::Facebook,
|
||||
Platform::Instagram,
|
||||
Platform::InstagramFacebook,
|
||||
Platform::Threads,
|
||||
Platform::Pinterest,
|
||||
Platform::Bluesky,
|
||||
Platform::Mastodon,
|
||||
Platform::Telegram,
|
||||
Platform::Discord,
|
||||
]);
|
||||
|
||||
test('disabling every platform yields no enabled queues', function () {
|
||||
foreach (Platform::cases() as $platform) {
|
||||
config(["trypost.platforms.{$platform->value}.enabled" => false]);
|
||||
}
|
||||
|
||||
expect(Platform::enabledQueues())->toBe([]);
|
||||
});
|
||||
|
||||
test('enabledQueues is always a subset of allQueues', function () {
|
||||
config([
|
||||
'trypost.platforms.linkedin.enabled' => true,
|
||||
'trypost.platforms.linkedin-page.enabled' => false,
|
||||
'trypost.platforms.x.enabled' => false,
|
||||
'trypost.platforms.tiktok.enabled' => true,
|
||||
'trypost.platforms.youtube.enabled' => false,
|
||||
'trypost.platforms.facebook.enabled' => true,
|
||||
'trypost.platforms.instagram.enabled' => false,
|
||||
'trypost.platforms.instagram-facebook.enabled' => true,
|
||||
'trypost.platforms.threads.enabled' => false,
|
||||
'trypost.platforms.pinterest.enabled' => true,
|
||||
'trypost.platforms.bluesky.enabled' => false,
|
||||
'trypost.platforms.mastodon.enabled' => true,
|
||||
'trypost.platforms.telegram.enabled' => false,
|
||||
'trypost.platforms.discord.enabled' => true,
|
||||
]);
|
||||
|
||||
$enabledQueues = Platform::enabledQueues();
|
||||
|
||||
expect($enabledQueues)->toEqual(array_values(array_intersect(Platform::allQueues(), $enabledQueues)))
|
||||
->and(array_diff($enabledQueues, Platform::allQueues()))->toBe([]);
|
||||
});
|
||||
|
||||
test('horizon social publishing queues match enabledQueues', function () {
|
||||
expect(config('horizon.defaults.social-publishing.queue'))->toBe(Platform::enabledQueues());
|
||||
});
|
||||
|
||||
test('isEnabled falls back to env when enabled config is missing', function (Platform $platform, string $envKey) {
|
||||
$platforms = config('trypost.platforms');
|
||||
unset($platforms[$platform->value]['enabled']);
|
||||
config(['trypost.platforms' => $platforms]);
|
||||
|
||||
$original = getenv($envKey);
|
||||
putenv("{$envKey}=false");
|
||||
|
||||
try {
|
||||
expect($platform->isEnabled())->toBeFalse();
|
||||
} finally {
|
||||
if ($original === false) {
|
||||
putenv($envKey);
|
||||
} else {
|
||||
putenv("{$envKey}={$original}");
|
||||
}
|
||||
}
|
||||
})->with([
|
||||
[Platform::LinkedIn, 'LINKEDIN_ENABLED'],
|
||||
[Platform::LinkedInPage, 'LINKEDIN_PAGE_ENABLED'],
|
||||
[Platform::X, 'X_ENABLED'],
|
||||
[Platform::InstagramFacebook, 'INSTAGRAM_FACEBOOK_ENABLED'],
|
||||
[Platform::Telegram, 'TELEGRAM_ENABLED'],
|
||||
[Platform::Discord, 'DISCORD_ENABLED'],
|
||||
]);
|
||||
|
||||
test('linkedin pages and instagram facebook are not directly connectable', function () {
|
||||
expect(Platform::LinkedInPage->isConnectable())->toBeFalse();
|
||||
expect(Platform::LinkedIn->isConnectable())->toBeTrue();
|
||||
|
|
|
|||
Loading…
Reference in a new issue