feat: application sidekicks = non-HTTP workers with shared state#2287
feat: application sidekicks = non-HTTP workers with shared state#2287nicolas-grekas wants to merge 1 commit intophp:mainfrom
Conversation
e1655ab to
867e9b3
Compare
|
Interesting approach to parallelism, what would be a concrete use case for only letting information flow one way from the sidekick to the http workers? Usually the flow would be inverted, where a http worker offloads work to a pool of 'sidekick' workers and can optionally wait for a task to complete. |
da54ab8 to
a06ba36
Compare
|
Thank you for the contribution. Interesting idea, but I'm thinking we should merge the approach with #1883. The kind of worker is the same, how they are started is but a detail. @nicolas-grekas the Caddyfile setting should likely be per |
ad71bfe to
05e9702
Compare
|
@AlliBalliBaba The use case isn't task offloading (HTTP->worker), but out-of-band reconfigurability (environment->worker->HTTP). Sidekicks observe external systems (Redis Sentinel failover, secret rotation, feature flag changes, etc.) and publish updated configuration that HTTP workers pick up on their next request; with per-request consistency guaranteed via Task offloading (what you describe) is a valid and complementary pattern, but it solves a different problem. The non-HTTP worker foundation here could support both. @henderkes Agreed that the underlying non-HTTP worker type overlaps with #1883. The foundation (skip HTTP startup/shutdown, immediate readiness, cooperative shutdown) is the same. The difference is the API layer and the DX goals:
Happy to follow up with your proposals now that this is hopefully clarified. |
05e9702 to
8a56d4c
Compare
|
Great PR! Couldn't we create a single API that covers both use case? We try to keep the number of public symbols and config option as small as possible! |
Yes, that's why I'd like to unify the two API's and background implementations into one. Unfortunately the first task worker attempt didn't make it into |
|
The PHP-side API has been significantly reworked since the initial iteration: I replaced The old design used
Key improvements:
Other changes:
|
cb65f46 to
4dda455
Compare
|
Thanks @dunglas and @henderkes for the feedback. I share the goal of keeping the API surface minimal. Thinking about it more, the current API is actually quite small and already general:
The name "sidekick" works as a generic concept: a helper running alongside. The current set_vars/get_vars protocol covers the config-publishing use case. For task offloading (HTTP->worker) later, the same sidekick infrastructure could support:
Same worker type, same So the path would be:
The foundation (non-HTTP threads, cooperative shutdown, crash recovery, per-php_server scoping) is shared. Only the communication primitives differ. WDYT? |
b3734f5 to
ed79f46
Compare
|
I think the failures are unrelated - a cache reset would be needed. Any help on this topic? |
|
Hmm, it seems they are on some versions, for example here: https://github.com/php/frankenphp/actions/runs/23192689128/job/67392820942?pr=2287#step:10:3614 For the cache, I'm not aware of a Github feature that allow to clear everything unfortunately 🙁 |
ed79f46 to
fe77b5a
Compare
fe77b5a to
7556610
Compare
My only worry with this is that "sidekick" implies that there's a "main" character related to it. That's the case here, but wouldn't necessarily be the case for task- or extension workers. Other than the naming, I don't object the api. |
Add support for "sidekick" workers: long-running PHP scripts that run outside the HTTP request cycle, observe their environment, and publish configuration to HTTP workers in real time.
This enables patterns like Redis Sentinel discovery, secret rotation, feature flag streaming, and cache invalidation — without polling, TTLs, or redeployment.
New PHP functions
frankenphp_sidekick_get_vars(string|array $name, float $timeout = 30.0): arrayStarts a sidekick and returns its published variables. The first call blocks until the sidekick calls
set_vars()or the timeout expires. Subsequent calls return the latest snapshot immediately. When given an array of names, all sidekicks are started in parallel and vars are returned keyed by name. Works in both worker and non-worker mode.frankenphp_sidekick_set_vars(array $vars): voidPublishes a snapshot of variables from inside a sidekick script. All keys and values must be strings. Each call replaces the entire snapshot atomically. Can only be called from a sidekick context.
frankenphp_sidekick_should_stop(): boolCooperative shutdown check. Sidekick scripts poll this in their event loop to exit gracefully when FrankenPHP shuts down. Can only be called from a sidekick context.
Caddyfile configuration
How it works
Design highlights
get_varsblocks until the sidekick has published its initial stateset_varsreplaces all vars at once — no partial state$_SERVERinjectionset_varsandshould_stopthrow if not called from a sidekickget_varsfrom multiple HTTP workers — only one starts the sidekickget_vars(['a', 'b'])starts all sidekicks concurrentlyphp_serverscoping: eachphp_serverblock has its ownSidekickRegistry— different apps on the same Caddy instance are fully isolatedfunction_exists('frankenphp_sidekick_get_vars')lets the same code work with or without FrankenPHPget_varsworks from any PHP script served byphp_serverbin/consolecompatible: sidekick name is available as$_SERVER['argv'][1]and$_SERVER['FRANKENPHP_SIDEKICK_NAME']Runtime behavior
SCRIPT_FILENAMEis set correctly for non-.phpentrypoints#!/usr/bin/env php) are silently skipped