Tento soubor poskytuje vodítka pro Claude Code (claude.ai/code) při práci s kódem v tomto repozitáři.
adt/background-queue je samostatná PHP knihovna (PHP ^8.2) pro zpracování úloh na pozadí, a to buď přes cron, nebo přes AMQP broker (RabbitMQ). Jádro je nezávislé na frameworku - navzdory zmínce o "Nette" v composer.json kód nemá žádnou závislost na Nette DI. Joby ukládá přes čisté Doctrine DBAL (nikoli ORM), pomocí vlastního Connection nezávislého na entity manageru hostitelské aplikace.
Uživatelská dokumentace (volby konfigurace, sémantika výjimek, příklady použití) je v README.md a je psaná česky; přečti si ji pro popis chování na uživatelské úrovni.
Vývojové prostředí běží v Dockeru (kontejnery background-queue_php, background-queue_mysql, background-queue_rabbitmq).
make docker-up # sestaví a spustí kontejnery (docker-compose up)
make init # composer install + (znovu)vytvoří MySQL databázi + vyčistí temp/
make test # spustí Integration suite: php vendor/bin/codecept run Integrationmake init spouští composer install uvnitř kontejneru background-queue_php. make test volá Codeception přímo, takže ho spouštěj zevnitř kontejneru (docker exec background-queue_php make test) nebo tam, kde je dostupné PHP + databáze.
Spuštění jednoho testu / jedné testovací metody:
php vendor/bin/codecept run Integration BackgroundQueueTest
php vendor/bin/codecept run Integration BackgroundQueueTest:testPublishExistuje jen jedna testovací suite, Integration (konfigurace v tests/Integration.suite.yml, namespace Tests). Testy jsou skutečné integrační testy proti MySQL; mnoho z nich je řízeno data providery a parametrizováno přes producer (broker zapnutý/vypnutý) a kombinace serialGroup/waiting. Tests\Support\Helper\Producer je falešný in-memory broker, který umožňuje otestovat brokerovou cestu bez RabbitMQ.
Jedna velká třída, která vlastní vše: konfiguraci, DBAL connection, publikování i zpracování. Klíčové vstupní body:
publish($callbackName, $parameters, $serialGroup, $identifier, $mode, $postponeBy, $priority)- vytvoříBackgroundJob, uloží ho (save()) a pak zavolápublishToBroker(). Název callbacku musí existovat v nakonfigurované mapěcallbacks.process()- cronová cesta. Vybere joby ve zpracovatelných stavech a buď je znovu vloží do brokera (broker mód), nebo je zpracuje inline (processJob()).processJob(int $id)- spustí callback jednoho jobu a aplikuje stavový automat výsledku. Volá ho jakprocess()(cron), tak brokerovýConsumer.
Téměř každá větev v process()/save() se odvíjí od toho, zda je nakonfigurován producer:
- Cron mód (bez producera):
process()spouští joby inline. Zpracovatelné stavy nezahrnujíSTATE_BACK_TO_BROKER. - Broker mód (producer nastaven):
process()joby nespouští; přepne způsobilé řádky v DB zpět naSTATE_READYa znovu publikuje jejich ID do brokera. Skutečná práce probíhá vConsumer::consume()→processJob(). Broker vždy nese pouze ID jobu (string); řádek v DB je vždy zdrojem pravdy.
Stavy jsou celočíselné konstanty na BackgroundJob (STATE_READY=1, STATE_PROCESSING=2, STATE_FINISHED=3, STATE_TEMPORARILY_FAILED=4, STATE_PERMANENTLY_FAILED=5, STATE_WAITING=6, STATE_REDUNDANT=7, STATE_BROKER_FAILED=8, STATE_BACK_TO_BROKER=-1). Dotazy řídí READY_TO_PROCESS_STATES a FINISHED_STATES.
Výsledek callbacku (switch v processJob()) je určen typem vyhozené výjimky:
PermanentErrorException/TypeError/ holáDieException→PERMANENTLY_FAILEDWaitingException→ znovu publikováno (klon) a ponecháno ve waiting; počítadlo pokusů se nezvyšujeSkipException→ tiše přeskočeno, stav zůstává- jakýkoli jiný
Throwable→TEMPORARILY_FAILED, opakováno s exponenciálním backoffem (getPostponement(), zdvojnásobování, strop 16 minut) - bez vyhození výjimky →
FINISHED
DieException s getPrevious() se přepošle podle té předchozí výjimky a nastaví shouldDie - konzumer se ukončí před další iterací (dieIfNecessary() kontrolováno ve smyčce ConsumeCommandu). Slouží k ukončení konzumera, kterému se zavřel Doctrine EM.
Joby sdílející serialGroup běží striktně v pořadí podle ID. checkUnfinishedJobs() hledá starší nedokončený job ve stejné skupině; pokud ho najde, aktuální job se odloží do STATE_WAITING. V broker módu interní opakující se job _processWaitingJobs (CallbackNameEnum::PROCESS_WAITING_JOBS, registrovaný automaticky při nastaveném produceru) periodicky přepíná nejstarší WAITING job v každé skupině zpět na READY a znovu ho publikuje. Tento interní job je v módu RECURRING a po každém úspěšném běhu se z DB maže (jeho historie nemá hodnotu).
UNIQUE- vyžadujeidentifier;isRedundant()označí job jakoREDUNDANT, pokud existuje starší job se stejným identifikátorem.RECURRING- vyžadujeidentifier; přiFINISHEDse job naklonuje a znovu publikuje (cloneAndPublish()), ale jen pokud už neexistuje nedokončený job s tímto identifikátorem.
BackgroundJob ukládá parametry do jednoho ze dvou sloupců: parameters_json (když platí isJsonable() - pouze skaláry/pole/DateTimeInterface) nebo parameters (BLOB, PHP serialize(), podporuje libovolné objekty a binární data). getParameters() čte ten, který je nastaven; JSON cesta rehydratuje DateTime přes ADT\Utils\Utils::getDateTimeFromArray.
updateSchema() sestaví definici tabulky v kódu a porovná ji s živým schématem (vytvoření nebo ALTER). Volá se automaticky při prvním použití; nejsou žádné migrační soubory. Název tabulky je konfigurovatelný (tableName, výchozí background_job).
- Během publishování queue znovu používá connection aplikace, aby inserty jobů sdílely transakci aplikace.
- Během processJob vytvoří
createConnection()samostatné DBAL connection ze stejných parametrů, aby v případě rollbacku aplikační transakce mohl konzumer přesto zapsat chybový stav jobu. databaseConnectionCheckAndReconnect()/databasePing()chrání dlouhoběžící konzumery proti ztrátě spojení s DB.
BackgroundQueueMiddleware je Doctrine DBAL Middleware, který si hostitelská aplikace instaluje na své vlastní connection. Obaluje commit() tak, že publikování do brokera (doPublishToBroker()) je odloženo, dokud se skutečně nezacommituje nejvíce vnější aplikační transakce (transactionNestingLevel === 0). Tím se zabrání publikování ID jobu do RabbitMQ dříve, než je řádek trvale zacommitován. publishToBroker() podobně bufferuje, dokud je aktivní transakce.
Producer a Consumer jsou rozhraní - přines si libovolný broker. K dispozici je hotová implementace PhpAmqpLib (volitelná závislost; viz README pro doporučené omezení conflict pinující php-amqplib na ^3.0). Priorita je modelována jako samostatné fronty pojmenované <queue>_<priority>; QUEUE_TOP_PRIORITY = 0 je vyhrazena pro řídicí (DIE) zprávy, takže ji konzumeři kontrolují jako první. publishDie() + tělo DIE je způsob, jak reload-consumers elegantně restartuje běžící konzumery.
Všechny příkazy kromě ConsumeCommand rozšiřují lokální abstraktní Command, který obaluje běh do ADT\CommandLock (FileSystemStorage pod tempDir), takže nemohou běžet souběžně samy se sebou.
background-queue:process- vstupní bod pro cron (spouštět každou minutu).background-queue:consume [queue] -j <jobs> -p <priorities>- dlouhoběžící brokerový konzumer;-ppřijímá rozsahy jako20-40,25-,-20.background-queue:clear-finished [days],background-queue:reload-consumers <number> [queue],background-queue:update-schema.
startBulk() / endBulk() s bulkSize dávkují více volání publish() do jediného INSERTu s více VALUES (insertMultipleEntities()), zabaleno do transakce, aby bylo možné získat vložená ID.