[{"data":1,"prerenderedAt":2333},["ShallowReactive",2],{"navigation_docs_en":3,"-en-kit-worker":441,"-en-kit-worker-surround":2328},[4,45,60,81,112,129,146,173,197,213,242,279,356,372,384,403,429],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"queue","i-lucide-layers","\u002Fen\u002Fqueue","en\u002F10.queue",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Overview","\u002Fen\u002Fqueue\u002Foverview","en\u002F10.queue\u002F1.overview","i-lucide-list-ordered",{"title":57,"path":58,"stem":59,"icon":47},"Tasks","\u002Fen\u002Fqueue\u002Ftasks","en\u002F10.queue\u002F2.tasks",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":44},"events","i-lucide-workflow","\u002Fen\u002Fevents","en\u002F11.events",[66,71,76],{"title":67,"path":68,"stem":69,"icon":70},"Events","\u002Fen\u002Fevents\u002Foverview","en\u002F11.events\u002F1.overview","i-lucide-radio",{"title":72,"path":73,"stem":74,"icon":75},"Outbox","\u002Fen\u002Fevents\u002Foutbox","en\u002F11.events\u002F2.outbox","i-lucide-inbox",{"title":77,"path":78,"stem":79,"icon":80},"Scheduling","\u002Fen\u002Fevents\u002Fscheduler","en\u002F11.events\u002F3.scheduler","i-lucide-calendar-clock",{"title":82,"icon":83,"path":84,"stem":85,"children":86,"page":44},"auth","i-lucide-shield-check","\u002Fen\u002Fauth","en\u002F12.auth",[87,92,97,102,107],{"title":88,"path":89,"stem":90,"icon":91},"Authentication","\u002Fen\u002Fauth\u002Foverview","en\u002F12.auth\u002F1.overview","i-lucide-key-round",{"title":93,"path":94,"stem":95,"icon":96},"RBAC","\u002Fen\u002Fauth\u002Frbac","en\u002F12.auth\u002F2.rbac","i-lucide-users",{"title":98,"path":99,"stem":100,"icon":101},"Policies","\u002Fen\u002Fauth\u002Fpolicy","en\u002F12.auth\u002F3.policy","i-lucide-gavel",{"title":103,"path":104,"stem":105,"icon":106},"Using auth standalone","\u002Fen\u002Fauth\u002Fstandalone","en\u002F12.auth\u002F4.standalone","i-lucide-plug",{"title":108,"path":109,"stem":110,"icon":111},"Social login with goth, standalone","\u002Fen\u002Fauth\u002Fsocial-standalone","en\u002F12.auth\u002F5.social-standalone","i-lucide-log-in",{"title":113,"icon":114,"path":115,"stem":116,"children":117,"page":44},"mail","i-lucide-mail","\u002Fen\u002Fmail","en\u002F13.mail",[118,121,125],{"title":52,"path":119,"stem":120,"icon":114},"\u002Fen\u002Fmail\u002Foverview","en\u002F13.mail\u002F1.overview",{"title":122,"path":123,"stem":124,"icon":114},"SMTP","\u002Fen\u002Fmail\u002Fsmtp","en\u002F13.mail\u002F2.smtp",{"title":126,"path":127,"stem":128,"icon":114},"MJML","\u002Fen\u002Fmail\u002Fmjml","en\u002F13.mail\u002F3.mjml",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":44},"notify","i-lucide-bell","\u002Fen\u002Fnotify","en\u002F14.notify",[135,138,142],{"title":52,"path":136,"stem":137,"icon":131},"\u002Fen\u002Fnotify\u002Foverview","en\u002F14.notify\u002F1.overview",{"title":139,"path":140,"stem":141,"icon":131},"Database channel","\u002Fen\u002Fnotify\u002Fdatabase","en\u002F14.notify\u002F2.database",{"title":143,"path":144,"stem":145,"icon":131},"Broadcast channel","\u002Fen\u002Fnotify\u002Fbroadcast","en\u002F14.notify\u002F3.broadcast",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":44},"storage","i-lucide-hard-drive","\u002Fen\u002Fstorage","en\u002F15.storage",[152,155,159,163,168],{"title":52,"path":153,"stem":154,"icon":148},"\u002Fen\u002Fstorage\u002Foverview","en\u002F15.storage\u002F1.overview",{"title":156,"path":157,"stem":158},"Disk API","\u002Fen\u002Fstorage\u002Fdisk-api","en\u002F15.storage\u002F2.disk-api",{"title":160,"path":161,"stem":162,"icon":148},"Memory and local disk","\u002Fen\u002Fstorage\u002Flocal","en\u002F15.storage\u002F3.local",{"title":164,"path":165,"stem":166,"icon":167},"S3","\u002Fen\u002Fstorage\u002Fs3","en\u002F15.storage\u002F4.s3","i-lucide-cloud",{"title":169,"path":170,"stem":171,"icon":172},"Testing","\u002Fen\u002Fstorage\u002Ftesting","en\u002F15.storage\u002F5.testing","i-lucide-flask-conical",{"title":174,"icon":175,"path":176,"stem":177,"children":178,"page":44},"telemetry","i-lucide-activity","\u002Fen\u002Ftelemetry","en\u002F16.telemetry",[179,182,187,192],{"title":52,"path":180,"stem":181,"icon":175},"\u002Fen\u002Ftelemetry\u002Foverview","en\u002F16.telemetry\u002F1.overview",{"title":183,"path":184,"stem":185,"icon":186},"Logging","\u002Fen\u002Ftelemetry\u002Flogging","en\u002F16.telemetry\u002F2.logging","i-lucide-scroll-text",{"title":188,"path":189,"stem":190,"icon":191},"OpenTelemetry","\u002Fen\u002Ftelemetry\u002Fopentelemetry","en\u002F16.telemetry\u002F3.opentelemetry","i-lucide-radar",{"title":193,"path":194,"stem":195,"icon":196},"Sentry","\u002Fen\u002Ftelemetry\u002Fsentry","en\u002F16.telemetry\u002F4.sentry","i-lucide-bug",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F17.reference",[203,208],{"title":204,"path":205,"stem":206,"icon":207},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F17.reference\u002F1.configuration","i-lucide-settings",{"title":209,"path":210,"stem":211,"icon":212},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F17.reference\u002F2.dependencies","i-lucide-package",{"title":214,"icon":215,"path":216,"stem":217,"children":218,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[219,223,228,232,237],{"title":220,"path":221,"stem":222,"icon":47},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture",{"title":224,"path":225,"stem":226,"icon":227},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F2.error-model","i-lucide-shield-alert",{"title":204,"path":229,"stem":230,"icon":231},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F3.configuration","i-lucide-settings-2",{"title":233,"path":234,"stem":235,"icon":236},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F4.codegen-pipeline","i-lucide-file-json",{"title":238,"path":239,"stem":240,"icon":241},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F5.design-patterns","i-lucide-puzzle",{"title":243,"icon":244,"path":245,"stem":246,"children":247,"page":44},"Kit","i-lucide-box","\u002Fen\u002Fkit","en\u002F3.kit",[248,251,256,261,266,270,275],{"title":52,"path":249,"stem":250,"icon":244},"\u002Fen\u002Fkit\u002Foverview","en\u002F3.kit\u002F1.overview",{"title":252,"path":253,"stem":254,"icon":255},"Application lifecycle","\u002Fen\u002Fkit\u002Fapp","en\u002F3.kit\u002F2.app","i-lucide-power",{"title":257,"path":258,"stem":259,"icon":260},"Server","\u002Fen\u002Fkit\u002Fserver","en\u002F3.kit\u002F3.server","i-lucide-server",{"title":262,"path":263,"stem":264,"icon":265},"Worker","\u002Fen\u002Fkit\u002Fworker","en\u002F3.kit\u002F4.worker","i-lucide-cog",{"title":267,"path":268,"stem":269,"icon":70},"Realtime","\u002Fen\u002Fkit\u002Frealtime","en\u002F3.kit\u002F5.realtime",{"title":271,"path":272,"stem":273,"icon":274},"Migrations","\u002Fen\u002Fkit\u002Fmigrations","en\u002F3.kit\u002F6.migrations","i-lucide-file-stack",{"title":276,"path":277,"stem":278,"icon":111},"Social login","\u002Fen\u002Fkit\u002Fsocial-login","en\u002F3.kit\u002F7.social-login",{"title":280,"icon":281,"path":282,"stem":283,"children":284,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F4.cli",[285,288,293,298,303,308,313,318,323,328,333,337,342,347,351],{"title":52,"path":286,"stem":287,"icon":281},"\u002Fen\u002Fcli\u002Foverview","en\u002F4.cli\u002F1.overview",{"title":289,"path":290,"stem":291,"icon":292},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F4.cli\u002F10.add-db","i-lucide-database-zap",{"title":294,"path":295,"stem":296,"icon":297},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F4.cli\u002F11.add-compose","i-lucide-container",{"title":299,"path":300,"stem":301,"icon":302},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F4.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":304,"path":305,"stem":306,"icon":307},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F4.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":309,"path":310,"stem":311,"icon":312},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F4.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":314,"path":315,"stem":316,"icon":317},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F4.cli\u002F15.add-seeder","i-lucide-sprout",{"title":319,"path":320,"stem":321,"icon":322},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F4.cli\u002F2.new-project","i-lucide-folder-plus",{"title":324,"path":325,"stem":326,"icon":327},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F4.cli\u002F3.new-module","i-lucide-blocks",{"title":329,"path":330,"stem":331,"icon":332},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F4.cli\u002F4.add-surface","i-lucide-layers-2",{"title":334,"path":335,"stem":336,"icon":244},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F4.cli\u002F5.add-core",{"title":338,"path":339,"stem":340,"icon":341},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F4.cli\u002F6.add-handler","i-lucide-webhook",{"title":343,"path":344,"stem":345,"icon":346},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F4.cli\u002F7.new-migration","i-lucide-file-plus",{"title":348,"path":349,"stem":350,"icon":265},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F4.cli\u002F8.worker-generators",{"title":352,"path":353,"stem":354,"icon":355},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F4.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"title":357,"icon":227,"path":358,"stem":359,"children":360,"page":44},"errs","\u002Fen\u002Ferrs","en\u002F5.errs",[361,364,368],{"title":52,"path":362,"stem":363,"icon":227},"\u002Fen\u002Ferrs\u002Foverview","en\u002F5.errs\u002F1.overview",{"title":365,"path":366,"stem":367},"API","\u002Fen\u002Ferrs\u002Fapi","en\u002F5.errs\u002F2.api",{"title":369,"path":370,"stem":371,"icon":227},"Integration","\u002Fen\u002Ferrs\u002Fintegration","en\u002F5.errs\u002F3.integration",{"title":373,"icon":231,"path":374,"stem":375,"children":376,"page":44},"envconf","\u002Fen\u002Fenvconf","en\u002F6.envconf",[377,380],{"title":52,"path":378,"stem":379,"icon":231},"\u002Fen\u002Fenvconf\u002Foverview","en\u002F6.envconf\u002F1.overview",{"title":381,"path":382,"stem":383},"Recipes","\u002Fen\u002Fenvconf\u002Frecipes","en\u002F6.envconf\u002F2.recipes",{"title":385,"icon":386,"path":387,"stem":388,"children":389,"page":44},"httperr","i-lucide-globe","\u002Fen\u002Fhttperr","en\u002F7.httperr",[390,393,398],{"title":52,"path":391,"stem":392,"icon":386},"\u002Fen\u002Fhttperr\u002Foverview","en\u002F7.httperr\u002F1.overview",{"title":394,"path":395,"stem":396,"icon":397},"Error responses","\u002Fen\u002Fhttperr\u002Ferror-responses","en\u002F7.httperr\u002F2.error-responses","i-lucide-octagon-alert",{"title":399,"path":400,"stem":401,"icon":402},"Validation","\u002Fen\u002Fhttperr\u002Fvalidation","en\u002F7.httperr\u002F3.validation","i-lucide-badge-check",{"title":404,"icon":405,"path":406,"stem":407,"children":408,"page":44},"dbx","i-lucide-database","\u002Fen\u002Fdbx","en\u002F8.dbx",[409,412,416,420,425],{"title":52,"path":410,"stem":411,"icon":405},"\u002Fen\u002Fdbx\u002Foverview","en\u002F8.dbx\u002F1.overview",{"title":413,"path":414,"stem":415,"icon":106},"pgx","\u002Fen\u002Fdbx\u002Fpg","en\u002F8.dbx\u002F2.pg",{"title":417,"path":418,"stem":419,"icon":47},"bun","\u002Fen\u002Fdbx\u002Fbunx","en\u002F8.dbx\u002F3.bunx",{"title":421,"path":422,"stem":423,"icon":424},"Transactions","\u002Fen\u002Fdbx\u002Ftransactions","en\u002F8.dbx\u002F4.transactions","i-lucide-git-merge",{"title":426,"path":427,"stem":428,"icon":317},"Seeders","\u002Fen\u002Fdbx\u002Fseed","en\u002F8.dbx\u002F5.seed",{"title":430,"icon":55,"path":431,"stem":432,"children":433,"page":44},"paginate","\u002Fen\u002Fpaginate","en\u002F9.paginate",[434,437],{"title":52,"path":435,"stem":436,"icon":55},"\u002Fen\u002Fpaginate\u002Foverview","en\u002F9.paginate\u002F1.overview",{"title":438,"path":439,"stem":440,"icon":19},"Cursor pagination","\u002Fen\u002Fpaginate\u002Fcursor","en\u002F9.paginate\u002F2.cursor",{"id":442,"title":262,"body":443,"description":2321,"extension":2322,"links":2323,"meta":2324,"navigation":2325,"path":263,"seo":2326,"stem":264,"__hash__":2327},"docs_en\u002Fen\u002F3.kit\u002F4.worker.md",{"type":444,"value":445,"toc":2307},"minimark",[446,459,488,506,540,563,570,587,1294,1328,1331,1423,1450,1455,1461,1530,1544,1548,1561,1595,1602,1606,1675,1682,1830,1834,1859,1875,1893,1896,1907,1978,2074,2082,2132,2137,2140,2147,2188,2199,2203,2206,2235,2242,2246,2279,2303],[447,448,449,450,454,455,458],"p",{},"The worker chassis runs your background processing (events, listeners, scheduled jobs) as its own binary, ",[451,452,453],"code",{},"cmd\u002Fworker",", alongside the HTTP API. You import it the same way you import ",[451,456,457],{},"server",":",[460,461,466],"pre",{"className":462,"code":463,"language":464,"meta":465,"style":465},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fgpsystem\u002Fworker\"\n","go","",[451,467,468],{"__ignoreMap":465},[469,470,473,477,481,485],"span",{"class":471,"line":472},"line",1,[469,474,476],{"class":475},"s7zQu","import",[469,478,480],{"class":479},"sMK4o"," \"",[469,482,484],{"class":483},"sBMFI","github.com\u002Fgp-system\u002Fgpsystem\u002Fworker",[469,486,487],{"class":479},"\"\n",[447,489,490,493,494,497,498,505],{},[451,491,492],{},"worker.Run"," is the background-work twin of the HTTP engines' ",[451,495,496],{},"Run",": the same ",[499,500,501,504],"a",{"href":253},[451,502,503],{},"app"," lifecycle"," (signal handling, graceful shutdown, closers, telemetry flush), except that instead of an HTTP server it runs three things in one binary:",[507,508,509,523,532],"ul",{},[510,511,512,513,517,518,522],"li",{},"the ",[514,515,516],"strong",{},"asynq server",": processing the ",[499,519,521],{"href":520},"\u002Fen\u002Fqueue\u002Ftasks#task-types","event, listener, scheduled and job tasks",";",[510,524,512,525,531],{},[514,526,527],{},[499,528,530],{"href":529},"\u002Fen\u002Fevents\u002Foutbox#the-relay","outbox relay",": forwarding committed events to the queue;",[510,533,512,534,539],{},[514,535,536],{},[499,537,538],{"href":78},"schedule runner",": firing the cron entries, with leader election.",[447,541,542,543,546,547,549,550,552,553,556,557,562],{},"This runs task processing and scheduling ",[514,544,545],{},"in a single process",": instead of separate worker and scheduler deployments, you scale one ",[451,548,453],{}," binary. The binary comes with ",[451,551,319],{}," by default; for a project scaffolded with ",[451,554,555],{},"new project --no-worker",", ",[499,558,559],{"href":349},[451,560,561],{},"add worker"," adds it. Either way, you deploy and scale it independently of the API.",[564,565,567,568],"h2",{"id":566},"the-generated-cmdworker","The generated ",[451,569,453],{},[447,571,572,573,575,576,575,579,582,583,586],{},"The shop's worker binary: every line written by the generator; modules are wired in by ",[451,574,324],{}," \u002F ",[451,577,578],{},"add listener",[451,580,581],{},"add job"," at the ",[451,584,585],{},"\u002F\u002F gpsystem:*"," anchors.",[460,588,591],{"className":462,"code":589,"filename":590,"language":464,"meta":465,"style":465},"package main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com\u002Fgp-system\u002Fdbx\u002Fpg\"\n    \"github.com\u002Fgp-system\u002Fenvconf\"\n    \"github.com\u002Fgp-system\u002Fevents\"\n    \"github.com\u002Fgp-system\u002Fevents\u002Foutbox\"\n    \"github.com\u002Fgp-system\u002Fevents\u002Fscheduler\"\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fworker\"\n\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\"\n    shopjobs \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\u002Fjobs\"\n    shoplisteners \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\u002Flisteners\"\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fplatform\u002Fconfig\"\n)\n\nfunc main() {\n    cfg := envconf.MustLoad[config.Config]()\n    ctx := context.Background()\n    pool := pg.MustNewPool(ctx, cfg.DB)\n    relay := outbox.MustNewRelay(ctx, cfg.Outbox, pool, cfg.Worker.Valkey)\n\n    err := worker.Run(ctx, cfg.Worker, func(reg *events.Registry, sched *scheduler.Schedule) error {\n        shopDeps := shop.Dependencies{\n            DB:         pg.NewDB(pool),\n            Transactor: pg.NewTransactor(pool),\n            Dispatcher: outbox.NewDispatcher(outbox.NewStore(pg.NewDB(pool))),\n        }\n        shoplisteners.Register(reg, shopDeps)\n        shopjobs.Register(sched, shopDeps)\n        return nil\n    },\n        worker.WithOutboxRelay(relay),\n        worker.WithCloser(\"pgxpool\", func(context.Context) error {\n            pool.Close()\n            return nil\n        }),\n    )\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n","cmd\u002Fworker\u002Fmain.go",[451,592,593,601,608,616,627,637,642,652,662,672,682,692,701,706,716,731,744,754,760,765,781,813,832,867,915,920,990,1009,1033,1054,1095,1101,1123,1144,1153,1159,1177,1214,1227,1235,1241,1247,1264,1282,1288],{"__ignoreMap":465},[469,594,595,598],{"class":471,"line":472},[469,596,597],{"class":479},"package",[469,599,600],{"class":483}," main\n",[469,602,604],{"class":471,"line":603},2,[469,605,607],{"emptyLinePlaceholder":606},true,"\n",[469,609,611,613],{"class":471,"line":610},3,[469,612,476],{"class":475},[469,614,615],{"class":479}," (\n",[469,617,619,622,625],{"class":471,"line":618},4,[469,620,621],{"class":479},"    \"",[469,623,624],{"class":483},"context",[469,626,487],{"class":479},[469,628,630,632,635],{"class":471,"line":629},5,[469,631,621],{"class":479},[469,633,634],{"class":483},"log",[469,636,487],{"class":479},[469,638,640],{"class":471,"line":639},6,[469,641,607],{"emptyLinePlaceholder":606},[469,643,645,647,650],{"class":471,"line":644},7,[469,646,621],{"class":479},[469,648,649],{"class":483},"github.com\u002Fgp-system\u002Fdbx\u002Fpg",[469,651,487],{"class":479},[469,653,655,657,660],{"class":471,"line":654},8,[469,656,621],{"class":479},[469,658,659],{"class":483},"github.com\u002Fgp-system\u002Fenvconf",[469,661,487],{"class":479},[469,663,665,667,670],{"class":471,"line":664},9,[469,666,621],{"class":479},[469,668,669],{"class":483},"github.com\u002Fgp-system\u002Fevents",[469,671,487],{"class":479},[469,673,675,677,680],{"class":471,"line":674},10,[469,676,621],{"class":479},[469,678,679],{"class":483},"github.com\u002Fgp-system\u002Fevents\u002Foutbox",[469,681,487],{"class":479},[469,683,685,687,690],{"class":471,"line":684},11,[469,686,621],{"class":479},[469,688,689],{"class":483},"github.com\u002Fgp-system\u002Fevents\u002Fscheduler",[469,691,487],{"class":479},[469,693,695,697,699],{"class":471,"line":694},12,[469,696,621],{"class":479},[469,698,484],{"class":483},[469,700,487],{"class":479},[469,702,704],{"class":471,"line":703},13,[469,705,607],{"emptyLinePlaceholder":606},[469,707,709,711,714],{"class":471,"line":708},14,[469,710,621],{"class":479},[469,712,713],{"class":483},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop",[469,715,487],{"class":479},[469,717,719,723,726,729],{"class":471,"line":718},15,[469,720,722],{"class":721},"sTEyZ","    shopjobs ",[469,724,725],{"class":479},"\"",[469,727,728],{"class":483},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\u002Fjobs",[469,730,487],{"class":479},[469,732,734,737,739,742],{"class":471,"line":733},16,[469,735,736],{"class":721},"    shoplisteners ",[469,738,725],{"class":479},[469,740,741],{"class":483},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\u002Flisteners",[469,743,487],{"class":479},[469,745,747,749,752],{"class":471,"line":746},17,[469,748,621],{"class":479},[469,750,751],{"class":483},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fplatform\u002Fconfig",[469,753,487],{"class":479},[469,755,757],{"class":471,"line":756},18,[469,758,759],{"class":479},")\n",[469,761,763],{"class":471,"line":762},19,[469,764,607],{"emptyLinePlaceholder":606},[469,766,768,771,775,778],{"class":471,"line":767},20,[469,769,770],{"class":479},"func",[469,772,774],{"class":773},"s2Zo4"," main",[469,776,777],{"class":479},"()",[469,779,780],{"class":479}," {\n",[469,782,784,787,790,793,796,799,802,805,807,810],{"class":471,"line":783},21,[469,785,786],{"class":721},"    cfg ",[469,788,789],{"class":479},":=",[469,791,792],{"class":721}," envconf",[469,794,795],{"class":479},".",[469,797,798],{"class":773},"MustLoad",[469,800,801],{"class":479},"[",[469,803,804],{"class":483},"config",[469,806,795],{"class":479},[469,808,809],{"class":483},"Config",[469,811,812],{"class":479},"]()\n",[469,814,816,819,821,824,826,829],{"class":471,"line":815},22,[469,817,818],{"class":721},"    ctx ",[469,820,789],{"class":479},[469,822,823],{"class":721}," context",[469,825,795],{"class":479},[469,827,828],{"class":773},"Background",[469,830,831],{"class":479},"()\n",[469,833,835,838,840,843,845,848,851,854,857,860,862,865],{"class":471,"line":834},23,[469,836,837],{"class":721},"    pool ",[469,839,789],{"class":479},[469,841,842],{"class":721}," pg",[469,844,795],{"class":479},[469,846,847],{"class":773},"MustNewPool",[469,849,850],{"class":479},"(",[469,852,853],{"class":721},"ctx",[469,855,856],{"class":479},",",[469,858,859],{"class":721}," cfg",[469,861,795],{"class":479},[469,863,864],{"class":721},"DB",[469,866,759],{"class":479},[469,868,870,873,875,878,880,883,885,887,889,891,893,895,897,900,902,904,906,908,910,913],{"class":471,"line":869},24,[469,871,872],{"class":721},"    relay ",[469,874,789],{"class":479},[469,876,877],{"class":721}," outbox",[469,879,795],{"class":479},[469,881,882],{"class":773},"MustNewRelay",[469,884,850],{"class":479},[469,886,853],{"class":721},[469,888,856],{"class":479},[469,890,859],{"class":721},[469,892,795],{"class":479},[469,894,72],{"class":721},[469,896,856],{"class":479},[469,898,899],{"class":721}," pool",[469,901,856],{"class":479},[469,903,859],{"class":721},[469,905,795],{"class":479},[469,907,262],{"class":721},[469,909,795],{"class":479},[469,911,912],{"class":721},"Valkey",[469,914,759],{"class":479},[469,916,918],{"class":471,"line":917},25,[469,919,607],{"emptyLinePlaceholder":606},[469,921,923,926,928,931,933,935,937,939,941,943,945,947,949,952,956,959,961,963,966,968,971,973,976,978,981,984,988],{"class":471,"line":922},26,[469,924,925],{"class":721},"    err ",[469,927,789],{"class":479},[469,929,930],{"class":721}," worker",[469,932,795],{"class":479},[469,934,496],{"class":773},[469,936,850],{"class":479},[469,938,853],{"class":721},[469,940,856],{"class":479},[469,942,859],{"class":721},[469,944,795],{"class":479},[469,946,262],{"class":721},[469,948,856],{"class":479},[469,950,951],{"class":479}," func(",[469,953,955],{"class":954},"sHdIc","reg",[469,957,958],{"class":479}," *",[469,960,61],{"class":483},[469,962,795],{"class":479},[469,964,965],{"class":483},"Registry",[469,967,856],{"class":479},[469,969,970],{"class":954}," sched",[469,972,958],{"class":479},[469,974,975],{"class":483},"scheduler",[469,977,795],{"class":479},[469,979,980],{"class":483},"Schedule",[469,982,983],{"class":479},")",[469,985,987],{"class":986},"spNyl"," error",[469,989,780],{"class":479},[469,991,993,996,998,1001,1003,1006],{"class":471,"line":992},27,[469,994,995],{"class":721},"        shopDeps ",[469,997,789],{"class":479},[469,999,1000],{"class":483}," shop",[469,1002,795],{"class":479},[469,1004,1005],{"class":483},"Dependencies",[469,1007,1008],{"class":479},"{\n",[469,1010,1012,1015,1017,1020,1022,1025,1027,1030],{"class":471,"line":1011},28,[469,1013,1014],{"class":721},"            DB",[469,1016,458],{"class":479},[469,1018,1019],{"class":721},"         pg",[469,1021,795],{"class":479},[469,1023,1024],{"class":773},"NewDB",[469,1026,850],{"class":479},[469,1028,1029],{"class":721},"pool",[469,1031,1032],{"class":479},"),\n",[469,1034,1036,1039,1041,1043,1045,1048,1050,1052],{"class":471,"line":1035},29,[469,1037,1038],{"class":721},"            Transactor",[469,1040,458],{"class":479},[469,1042,842],{"class":721},[469,1044,795],{"class":479},[469,1046,1047],{"class":773},"NewTransactor",[469,1049,850],{"class":479},[469,1051,1029],{"class":721},[469,1053,1032],{"class":479},[469,1055,1057,1060,1062,1064,1066,1069,1071,1074,1076,1079,1081,1084,1086,1088,1090,1092],{"class":471,"line":1056},30,[469,1058,1059],{"class":721},"            Dispatcher",[469,1061,458],{"class":479},[469,1063,877],{"class":721},[469,1065,795],{"class":479},[469,1067,1068],{"class":773},"NewDispatcher",[469,1070,850],{"class":479},[469,1072,1073],{"class":721},"outbox",[469,1075,795],{"class":479},[469,1077,1078],{"class":773},"NewStore",[469,1080,850],{"class":479},[469,1082,1083],{"class":721},"pg",[469,1085,795],{"class":479},[469,1087,1024],{"class":773},[469,1089,850],{"class":479},[469,1091,1029],{"class":721},[469,1093,1094],{"class":479},"))),\n",[469,1096,1098],{"class":471,"line":1097},31,[469,1099,1100],{"class":479},"        }\n",[469,1102,1104,1107,1109,1112,1114,1116,1118,1121],{"class":471,"line":1103},32,[469,1105,1106],{"class":721},"        shoplisteners",[469,1108,795],{"class":479},[469,1110,1111],{"class":773},"Register",[469,1113,850],{"class":479},[469,1115,955],{"class":721},[469,1117,856],{"class":479},[469,1119,1120],{"class":721}," shopDeps",[469,1122,759],{"class":479},[469,1124,1126,1129,1131,1133,1135,1138,1140,1142],{"class":471,"line":1125},33,[469,1127,1128],{"class":721},"        shopjobs",[469,1130,795],{"class":479},[469,1132,1111],{"class":773},[469,1134,850],{"class":479},[469,1136,1137],{"class":721},"sched",[469,1139,856],{"class":479},[469,1141,1120],{"class":721},[469,1143,759],{"class":479},[469,1145,1147,1150],{"class":471,"line":1146},34,[469,1148,1149],{"class":475},"        return",[469,1151,1152],{"class":479}," nil\n",[469,1154,1156],{"class":471,"line":1155},35,[469,1157,1158],{"class":479},"    },\n",[469,1160,1162,1165,1167,1170,1172,1175],{"class":471,"line":1161},36,[469,1163,1164],{"class":721},"        worker",[469,1166,795],{"class":479},[469,1168,1169],{"class":773},"WithOutboxRelay",[469,1171,850],{"class":479},[469,1173,1174],{"class":721},"relay",[469,1176,1032],{"class":479},[469,1178,1180,1182,1184,1187,1189,1191,1195,1197,1199,1201,1203,1205,1208,1210,1212],{"class":471,"line":1179},37,[469,1181,1164],{"class":721},[469,1183,795],{"class":479},[469,1185,1186],{"class":773},"WithCloser",[469,1188,850],{"class":479},[469,1190,725],{"class":479},[469,1192,1194],{"class":1193},"sfazB","pgxpool",[469,1196,725],{"class":479},[469,1198,856],{"class":479},[469,1200,951],{"class":479},[469,1202,624],{"class":483},[469,1204,795],{"class":479},[469,1206,1207],{"class":483},"Context",[469,1209,983],{"class":479},[469,1211,987],{"class":986},[469,1213,780],{"class":479},[469,1215,1217,1220,1222,1225],{"class":471,"line":1216},38,[469,1218,1219],{"class":721},"            pool",[469,1221,795],{"class":479},[469,1223,1224],{"class":773},"Close",[469,1226,831],{"class":479},[469,1228,1230,1233],{"class":471,"line":1229},39,[469,1231,1232],{"class":475},"            return",[469,1234,1152],{"class":479},[469,1236,1238],{"class":471,"line":1237},40,[469,1239,1240],{"class":479},"        }),\n",[469,1242,1244],{"class":471,"line":1243},41,[469,1245,1246],{"class":479},"    )\n",[469,1248,1250,1253,1256,1259,1262],{"class":471,"line":1249},42,[469,1251,1252],{"class":475},"    if",[469,1254,1255],{"class":721}," err ",[469,1257,1258],{"class":479},"!=",[469,1260,1261],{"class":479}," nil",[469,1263,780],{"class":479},[469,1265,1267,1270,1272,1275,1277,1280],{"class":471,"line":1266},43,[469,1268,1269],{"class":721},"        log",[469,1271,795],{"class":479},[469,1273,1274],{"class":773},"Fatal",[469,1276,850],{"class":479},[469,1278,1279],{"class":721},"err",[469,1281,759],{"class":479},[469,1283,1285],{"class":471,"line":1284},44,[469,1286,1287],{"class":479},"    }\n",[469,1289,1291],{"class":471,"line":1290},45,[469,1292,1293],{"class":479},"}\n",[447,1295,1296,1297,1300,1301,1303,1304,1306,1307,1309,1310,1312,1313,575,1316,1319,1320,1323,1324,1327],{},"The pattern is the same as on the HTTP side: dependencies travel in an ",[514,1298,1299],{},"explicit"," ",[451,1302,1005],{}," struct, and the register callback receives the event ",[451,1305,965],{}," and the ",[451,1308,980],{},", filled by the ",[451,1311,1111],{}," functions of the modules' generated ",[451,1314,1315],{},"listeners",[451,1317,1318],{},"jobs"," subpackages. The worker's ",[451,1321,1322],{},"Dispatcher"," is the ",[499,1325,1326],{"href":73},"outbox one"," too: an event dispatched by a listener gets the same guarantee as one dispatched from an HTTP request.",[564,1329,496],{"id":1330},"run",[460,1332,1334],{"className":462,"code":1333,"language":464,"meta":465,"style":465},"func Run(ctx context.Context, cfg Config, register RegisterFunc, opts ...Option) error\n\ntype RegisterFunc func(reg *events.Registry, sched *scheduler.Schedule) error\n",[451,1335,1336,1384,1388],{"__ignoreMap":465},[469,1337,1338,1340,1343,1345,1347,1349,1351,1353,1355,1357,1360,1362,1365,1368,1370,1373,1376,1379,1381],{"class":471,"line":472},[469,1339,770],{"class":479},[469,1341,1342],{"class":773}," Run",[469,1344,850],{"class":479},[469,1346,853],{"class":954},[469,1348,823],{"class":483},[469,1350,795],{"class":479},[469,1352,1207],{"class":483},[469,1354,856],{"class":479},[469,1356,859],{"class":954},[469,1358,1359],{"class":483}," Config",[469,1361,856],{"class":479},[469,1363,1364],{"class":954}," register",[469,1366,1367],{"class":483}," RegisterFunc",[469,1369,856],{"class":479},[469,1371,1372],{"class":954}," opts",[469,1374,1375],{"class":479}," ...",[469,1377,1378],{"class":483},"Option",[469,1380,983],{"class":479},[469,1382,1383],{"class":986}," error\n",[469,1385,1386],{"class":471,"line":603},[469,1387,607],{"emptyLinePlaceholder":606},[469,1389,1390,1393,1395,1397,1399,1401,1403,1405,1407,1409,1411,1413,1415,1417,1419,1421],{"class":471,"line":610},[469,1391,1392],{"class":479},"type",[469,1394,1367],{"class":483},[469,1396,951],{"class":479},[469,1398,955],{"class":954},[469,1400,958],{"class":479},[469,1402,61],{"class":483},[469,1404,795],{"class":479},[469,1406,965],{"class":483},[469,1408,856],{"class":479},[469,1410,970],{"class":954},[469,1412,958],{"class":479},[469,1414,975],{"class":483},[469,1416,795],{"class":479},[469,1418,980],{"class":483},[469,1420,983],{"class":479},[469,1422,1383],{"class":986},[447,1424,1425,1427,1428,1431,1432,1435,1436,1442,1443,1445,1446,1449],{},[451,1426,496],{},", in order: telemetry bootstrap (",[451,1429,1430],{},"telemetry.Setup",") → ",[451,1433,1434],{},"register"," builds the registry and the schedule → opens a ",[499,1437,1439],{"href":1438},"\u002Fen\u002Fqueue\u002Foverview#client",[451,1440,1441],{},"queue.Client"," (PING) → starts the asynq server, alongside the relay (when you passed ",[451,1444,1169],{},") and the schedule runner (when the schedule is non-empty; an empty schedule means no leader election either) → blocks until SIGINT\u002FSIGTERM. Returns ",[451,1447,1448],{},"nil"," on a clean shutdown.",[1451,1452,1454],"h3",{"id":1453},"what-processes-the-tasks","What processes the tasks",[447,1456,1457,1458,458],{},"The worker registers four built-in handlers on the ",[499,1459,1460],{"href":520},"task-type prefixes",[1462,1463,1464,1477],"table",{},[1465,1466,1467],"thead",{},[1468,1469,1470,1474],"tr",{},[1471,1472,1473],"th",{},"Task",[1471,1475,1476],{},"What the handler does",[1478,1479,1480,1495,1510,1520],"tbody",{},[1468,1481,1482,1488],{},[1483,1484,1485],"td",{},[451,1486,1487],{},"event:\u003Cname>",[1483,1489,1490,1491,1494],{},"fan-out: enqueues one ",[451,1492,1493],{},"listener:"," task per listener, with a deterministic TaskID",[1468,1496,1497,1502],{},[1483,1498,1499],{},[451,1500,1501],{},"listener:\u003Cevent>:\u003Cname>",[1483,1503,1504,1505,1509],{},"decodes the envelope, puts the ",[499,1506,1508],{"href":1507},"\u002Fen\u002Fevents\u002Foverview#the-envelope-and-the-delivery-metadata","Meta"," on the context, invokes your listener",[1468,1511,1512,1517],{},[1483,1513,1514],{},[451,1515,1516],{},"schedule:\u003Cevent>",[1483,1518,1519],{},"a scheduled fire: builds a fresh envelope (id = the trigger's task id) and fans out",[1468,1521,1522,1527],{},[1483,1523,1524],{},[451,1525,1526],{},"job:\u003Cname>",[1483,1528,1529],{},"invokes the scheduled job's handler",[447,1531,1532,1533,1536,1537,1539,1540,1543],{},"Every handler runs through three middleware: a per-task Sentry hub (breadcrumb isolation), ",[514,1534,1535],{},"panic recovery"," (a panicking handler becomes an ",[451,1538,357],{}," error and retries instead of killing the worker), and tracing: the consumer span continues the envelope's trace context, so the ",[499,1541,1542],{"href":189},"whole path is one trace",". A task with no registered handler (say, a deploy removed the listener) is skipped with a warning rather than retried forever.",[1451,1545,1547],{"id":1546},"shutdown-order","Shutdown order",[447,1549,1550,1551,1554,1555,556,1558,1560],{},"On ",[451,1552,1553],{},"SIGINT","\u002F",[451,1556,1557],{},"SIGTERM",[451,1559,496],{}," shuts down in a deterministic order:",[1562,1563,1564,1570,1580,1589],"ol",{},[510,1565,1566,1569],{},[514,1567,1568],{},"the producing loops stop",": the schedule runner (releasing its lease) and the relay's poll loop;",[510,1571,1572,1575,1576,1579],{},[514,1573,1574],{},"in-flight tasks drain",": for at most ",[451,1577,1578],{},"WORKER_SHUTDOWN_TIMEOUT","; whatever doesn't finish is requeued by asynq and redelivered later (one more reason for idempotent listeners);",[510,1581,1582,1585,1586,1588],{},[514,1583,1584],{},"closers run LIFO",": first the kit's own (scheduler, relay, queue client), then your ",[451,1587,1186],{},"s (in the shop, the pgx pool);",[510,1590,1591,1594],{},[514,1592,1593],{},"telemetry flush",": the last spans and logs still go out.",[447,1596,1597,1598,1601],{},"It is the same guarantee as on the HTTP side: a deploy doesn't cut an e-mail send in half. See the ",[499,1599,1600],{"href":253},"application lifecycle"," for details.",[564,1603,1605],{"id":1604},"options","Options",[1462,1607,1608,1617],{},[1465,1609,1610],{},[1468,1611,1612,1614],{},[1471,1613,1378],{},[1471,1615,1616],{},"Effect",[1478,1618,1619,1632,1642,1655,1665],{},[1468,1620,1621,1626],{},[1483,1622,1623],{},[451,1624,1625],{},"WithOutboxRelay(*outbox.Relay)",[1483,1627,1628,1629,1631],{},"runs the ",[499,1630,530],{"href":529}," inside this worker (safe on N replicas)",[1468,1633,1634,1639],{},[1483,1635,1636],{},[451,1637,1638],{},"WithCloser(name, fn)",[1483,1640,1641],{},"cleanup during graceful shutdown, after tasks have drained (LIFO)",[1468,1643,1644,1649],{},[1483,1645,1646],{},[451,1647,1648],{},"WithoutTelemetry()",[1483,1650,1651,1652,1654],{},"skips ",[451,1653,1430],{},"; for tests, or when the process configures it itself",[1468,1656,1657,1662],{},[1483,1658,1659],{},[451,1660,1661],{},"WithAsynqConfig(func(*asynq.Config))",[1483,1663,1664],{},"mutates the raw asynq config before the server is built; an escape hatch for settings the kit doesn't expose",[1468,1666,1667,1672],{},[1483,1668,1669],{},[451,1670,1671],{},"WithMux(func(*asynq.ServeMux))",[1483,1673,1674],{},"registers extra raw asynq handlers, for task types the kit doesn't model",[447,1676,1677,1678,1681],{},"The two escape hatches are the worker-side half of the kit's ",[499,1679,1680],{"href":53},"\"centralize, don't abstract\""," principle: when you need an asynq capability, you don't have to leave the chassis.",[460,1683,1685],{"className":462,"code":1684,"language":464,"meta":465,"style":465},"worker.Run(ctx, cfg.Worker, register,\n    worker.WithAsynqConfig(func(c *asynq.Config) {\n        c.HealthCheckFunc = reportHealth\n    }),\n    worker.WithMux(func(mux *asynq.ServeMux) {\n        mux.HandleFunc(\"import:products\", handleProductImport) \u002F\u002F your own task type\n    }),\n)\n",[451,1686,1687,1715,1744,1760,1765,1792,1822,1826],{"__ignoreMap":465},[469,1688,1689,1692,1694,1696,1698,1700,1702,1704,1706,1708,1710,1712],{"class":471,"line":472},[469,1690,1691],{"class":721},"worker",[469,1693,795],{"class":479},[469,1695,496],{"class":773},[469,1697,850],{"class":479},[469,1699,853],{"class":721},[469,1701,856],{"class":479},[469,1703,859],{"class":721},[469,1705,795],{"class":479},[469,1707,262],{"class":721},[469,1709,856],{"class":479},[469,1711,1364],{"class":721},[469,1713,1714],{"class":479},",\n",[469,1716,1717,1720,1722,1725,1728,1731,1733,1736,1738,1740,1742],{"class":471,"line":603},[469,1718,1719],{"class":721},"    worker",[469,1721,795],{"class":479},[469,1723,1724],{"class":773},"WithAsynqConfig",[469,1726,1727],{"class":479},"(func(",[469,1729,1730],{"class":954},"c",[469,1732,958],{"class":479},[469,1734,1735],{"class":483},"asynq",[469,1737,795],{"class":479},[469,1739,809],{"class":483},[469,1741,983],{"class":479},[469,1743,780],{"class":479},[469,1745,1746,1749,1751,1754,1757],{"class":471,"line":610},[469,1747,1748],{"class":721},"        c",[469,1750,795],{"class":479},[469,1752,1753],{"class":721},"HealthCheckFunc ",[469,1755,1756],{"class":479},"=",[469,1758,1759],{"class":721}," reportHealth\n",[469,1761,1762],{"class":471,"line":618},[469,1763,1764],{"class":479},"    }),\n",[469,1766,1767,1769,1771,1774,1776,1779,1781,1783,1785,1788,1790],{"class":471,"line":629},[469,1768,1719],{"class":721},[469,1770,795],{"class":479},[469,1772,1773],{"class":773},"WithMux",[469,1775,1727],{"class":479},[469,1777,1778],{"class":954},"mux",[469,1780,958],{"class":479},[469,1782,1735],{"class":483},[469,1784,795],{"class":479},[469,1786,1787],{"class":483},"ServeMux",[469,1789,983],{"class":479},[469,1791,780],{"class":479},[469,1793,1794,1797,1799,1802,1804,1806,1809,1811,1813,1816,1818],{"class":471,"line":639},[469,1795,1796],{"class":721},"        mux",[469,1798,795],{"class":479},[469,1800,1801],{"class":773},"HandleFunc",[469,1803,850],{"class":479},[469,1805,725],{"class":479},[469,1807,1808],{"class":1193},"import:products",[469,1810,725],{"class":479},[469,1812,856],{"class":479},[469,1814,1815],{"class":721}," handleProductImport",[469,1817,983],{"class":479},[469,1819,1821],{"class":1820},"sHwdD"," \u002F\u002F your own task type\n",[469,1823,1824],{"class":471,"line":644},[469,1825,1764],{"class":479},[469,1827,1828],{"class":471,"line":654},[469,1829,759],{"class":479},[564,1831,1833],{"id":1832},"queue-priorities","Queue priorities",[447,1835,1836,1837,1840,1841,1844,1845,1848,1849,1851,1852,1858],{},"The worker serves queues by weight: ",[451,1838,1839],{},"WORKER_QUEUES"," is a ",[451,1842,1843],{},"name:weight"," list, the counterpart of Horizon's ",[451,1846,1847],{},"balance","\u002Fqueue weighting. The shop puts its mail on a separate ",[451,1850,113],{}," queue (the listener's ",[499,1853,1855],{"href":1854},"\u002Fen\u002Fevents\u002Foverview#listeners",[451,1856,1857],{},"queue.OnQueue(\"mail\")"," registration), so a mail burst can't crowd out the rest of the work:",[460,1860,1864],{"className":1861,"code":1862,"language":1863,"meta":465,"style":465},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","WORKER_QUEUES=default:3,mail:1\n","sh",[451,1865,1866],{"__ignoreMap":465},[469,1867,1868,1870,1872],{"class":471,"line":472},[469,1869,1839],{"class":721},[469,1871,1756],{"class":479},[469,1873,1874],{"class":1193},"default:3,mail:1\n",[447,1876,1877,1878,1881,1882,1884,1885,1888,1889,1892],{},"With that, the worker spends ~75% of its capacity on ",[451,1879,1880],{},"default"," and ~25% on ",[451,1883,113],{}," whenever both have work. ",[451,1886,1887],{},"WORKER_STRICT_PRIORITY=true"," replaces weighting with a strict order: the highest-weight queue is drained completely before the next one is touched, useful when you have a ",[451,1890,1891],{},"critical"," queue that trumps everything.",[564,1894,204],{"id":1895},"configuration",[447,1897,1898,1899,1902,1903,1906],{},"You embed ",[451,1900,1901],{},"worker.Config"," without a prefix in the project config (ready-made in the generated ",[451,1904,1905],{},"config.go","):",[460,1908,1910],{"className":462,"code":1909,"language":464,"meta":465,"style":465},"type Config struct {\n    Worker worker.Config\n    DB     dbx.Config    `envPrefix:\"DB_\"`\n    Outbox outbox.Config `envPrefix:\"OUTBOX_\"`\n}\n",[451,1911,1912,1923,1935,1955,1974],{"__ignoreMap":465},[469,1913,1914,1916,1918,1921],{"class":471,"line":472},[469,1915,1392],{"class":479},[469,1917,1359],{"class":483},[469,1919,1920],{"class":479}," struct",[469,1922,780],{"class":479},[469,1924,1925,1928,1930,1932],{"class":471,"line":603},[469,1926,1927],{"class":721},"    Worker ",[469,1929,1691],{"class":483},[469,1931,795],{"class":479},[469,1933,1934],{"class":483},"Config\n",[469,1936,1937,1940,1942,1944,1946,1949,1952],{"class":471,"line":610},[469,1938,1939],{"class":721},"    DB     ",[469,1941,404],{"class":483},[469,1943,795],{"class":479},[469,1945,809],{"class":483},[469,1947,1948],{"class":479},"    `",[469,1950,1951],{"class":1193},"envPrefix:\"DB_\"",[469,1953,1954],{"class":479},"`\n",[469,1956,1957,1960,1962,1964,1966,1969,1972],{"class":471,"line":618},[469,1958,1959],{"class":721},"    Outbox ",[469,1961,1073],{"class":483},[469,1963,795],{"class":479},[469,1965,809],{"class":483},[469,1967,1968],{"class":479}," `",[469,1970,1971],{"class":1193},"envPrefix:\"OUTBOX_\"",[469,1973,1954],{"class":479},[469,1975,1976],{"class":471,"line":629},[469,1977,1293],{"class":479},[1462,1979,1980,1993],{},[1465,1981,1982],{},[1468,1983,1984,1987,1990],{},[1471,1985,1986],{},"Variable",[1471,1988,1989],{},"Default",[1471,1991,1992],{},"Meaning",[1478,1994,1995,2010,2027,2042,2056],{},[1468,1996,1997,2002,2007],{},[1483,1998,1999],{},[451,2000,2001],{},"WORKER_CONCURRENCY",[1483,2003,2004],{},[451,2005,2006],{},"10",[1483,2008,2009],{},"number of tasks processed simultaneously",[1468,2011,2012,2016,2021],{},[1483,2013,2014],{},[451,2015,1839],{},[1483,2017,2018],{},[451,2019,2020],{},"default:1",[1483,2022,2023,2024,983],{},"queue → weight pairs (",[451,2025,2026],{},"critical:6,default:3,low:1",[1468,2028,2029,2034,2039],{},[1483,2030,2031],{},[451,2032,2033],{},"WORKER_STRICT_PRIORITY",[1483,2035,2036],{},[451,2037,2038],{},"false",[1483,2040,2041],{},"strict priority order instead of weighting",[1468,2043,2044,2048,2053],{},[1483,2045,2046],{},[451,2047,1578],{},[1483,2049,2050],{},[451,2051,2052],{},"30s",[1483,2054,2055],{},"bounds the graceful drain; unfinished tasks are requeued",[1468,2057,2058,2063,2068],{},[1483,2059,2060],{},[451,2061,2062],{},"WORKER_DEFAULT_MAX_RETRY",[1483,2064,2065],{},[451,2066,2067],{},"25",[1483,2069,2070,2071],{},"retry budget for listener\u002Fjob tasks without their own ",[451,2072,2073],{},"queue.MaxRetry",[447,2075,2076,2078,2079,458],{},[451,2077,1901],{}," embeds three more configs, so the worker binary is configured ",[514,2080,2081],{},"from a single set of env vars",[507,2083,2084,2101,2113],{},[510,2085,2086,2092,2093,2096,2097,2100],{},[499,2087,2089],{"href":2088},"\u002Fen\u002Fqueue\u002Foverview#configuration",[451,2090,2091],{},"queue.Config"," under the ",[451,2094,2095],{},"VALKEY_"," prefix (",[451,2098,2099],{},"VALKEY_ADDR",", ...),",[510,2102,2103,2092,2109,2112],{},[499,2104,2106],{"href":2105},"\u002Fen\u002Fevents\u002Fscheduler#configuration",[451,2107,2108],{},"scheduler.Config",[451,2110,2111],{},"SCHEDULER_"," prefix,",[510,2114,2115,2118,2119,556,2122,556,2125,2128,2129,795],{},[451,2116,2117],{},"telemetry.Config"," without a prefix (the standard ",[451,2120,2121],{},"OTEL_*",[451,2123,2124],{},"LOG_*",[451,2126,2127],{},"SENTRY_*"," names); see the ",[499,2130,2131],{"href":180},"observability overview",[447,2133,2134,2135,795],{},"The full env reference: ",[499,2136,1895],{"href":205},[564,2138,183],{"id":2139},"logging",[447,2141,2142,2143,2146],{},"The worker logs per task through the default ",[451,2144,2145],{},"slog"," logger, trace-correlated with the consumer span:",[507,2148,2149,2165,2174],{},[510,2150,2151,2154,2155,556,2158,556,2161,2164],{},[451,2152,2153],{},"worker: task started"," (Info): with ",[451,2156,2157],{},"task_type",[451,2159,2160],{},"task_id",[451,2162,2163],{},"attempt"," attributes;",[510,2166,2167,2170,2171,522],{},[451,2168,2169],{},"worker: task done"," (Info): plus ",[451,2172,2173],{},"duration",[510,2175,2176,2179,2180,2183,2184,2187],{},[451,2177,2178],{},"worker: task failed"," (Error): plus ",[451,2181,2182],{},"error","; an intentional skip (",[451,2185,2186],{},"SkipRetry",", e.g. no registered handler) appears only once, as a warning.",[447,2189,2190,2191,2194,2195,2198],{},"asynq's own internal logs flow into the same pipeline. Where the lines end up (dev console, JSON stdout, OTLP) is decided by the ",[499,2192,2193],{"href":184},"logging config","; ",[451,2196,2197],{},"LOG_LEVEL=WARN"," silences the per-task Info lines.",[564,2200,2202],{"id":2201},"scaling","Scaling",[447,2204,2205],{},"The worker is stateless: run as many replicas as your workload needs, and all three components are replica-safe.",[507,2207,2208,2213,2226],{},[510,2209,512,2210,2212],{},[514,2211,516],{}," by nature: one task goes to one worker at a time;",[510,2214,512,2215,2217,2218,2225],{},[514,2216,530],{}," via the ",[499,2219,2221,2224],{"href":2220},"\u002Fen\u002Fevents\u002Foutbox#scalability-n-replicas-no-double-delivery",[451,2222,2223],{},"FOR UPDATE SKIP LOCKED"," + deterministic TaskID"," pair;",[510,2227,512,2228,2217,2230,2234],{},[514,2229,538],{},[499,2231,2233],{"href":2232},"\u002Fen\u002Fevents\u002Fscheduler#replica-safety-the-leader-election","Valkey-lease leader election",": one of N replicas fires.",[447,2236,2237,2238,795],{},"Nothing needs to be switched off or deployed separately to scale. The price is a single contract: delivery is at-least-once, so ",[499,2239,2241],{"href":2240},"\u002Fen\u002Fevents\u002Foverview#idempotency","listeners are idempotent",[564,2243,2245],{"id":2244},"running-locally","Running locally",[460,2247,2249],{"className":1861,"code":2248,"language":1863,"meta":465,"style":465},"go run .\u002Fcmd\u002Fmigrate up   # also applies the outbox_events migration\nmise run worker           # go run .\u002Fcmd\u002Fworker\n",[451,2250,2251,2267],{"__ignoreMap":465},[469,2252,2253,2255,2258,2261,2264],{"class":471,"line":472},[469,2254,464],{"class":483},[469,2256,2257],{"class":1193}," run",[469,2259,2260],{"class":1193}," .\u002Fcmd\u002Fmigrate",[469,2262,2263],{"class":1193}," up",[469,2265,2266],{"class":1820},"   # also applies the outbox_events migration\n",[469,2268,2269,2272,2274,2276],{"class":471,"line":603},[469,2270,2271],{"class":483},"mise",[469,2273,2257],{"class":1193},[469,2275,930],{"class":1193},[469,2277,2278],{"class":1820},"           # go run .\u002Fcmd\u002Fworker\n",[447,2280,2281,2282,2285,2286,2289,2290,556,2293,556,2295,556,2297,2299,2300,795],{},"Valkey comes from the project's ",[451,2283,2284],{},"compose.yml"," (",[451,2287,2288],{},"mise run dev","). For the generator commands (",[451,2291,2292],{},"add event",[451,2294,578],{},[451,2296,581],{},[451,2298,561],{},"), see the ",[499,2301,2302],{"href":349},"CLI worker generators",[2304,2305,2306],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":465,"searchDepth":603,"depth":603,"links":2308},[2309,2311,2315,2316,2317,2318,2319,2320],{"id":566,"depth":603,"text":2310},"The generated cmd\u002Fworker",{"id":1330,"depth":603,"text":496,"children":2312},[2313,2314],{"id":1453,"depth":610,"text":1454},{"id":1546,"depth":610,"text":1547},{"id":1604,"depth":603,"text":1605},{"id":1832,"depth":603,"text":1833},{"id":1895,"depth":603,"text":204},{"id":2139,"depth":603,"text":183},{"id":2201,"depth":603,"text":2202},{"id":2244,"depth":603,"text":2245},"The background-work chassis, asynq server, outbox relay and schedule runner in one binary, under a shared graceful shutdown.","md",null,{},{"icon":265},{"title":262,"description":2321},"HrNUnp8CMVXMJ3r0XLHYYodB4OLjPQiJe6F3brvP_ko",[2329,2331],{"title":257,"path":258,"stem":259,"description":2330,"icon":260,"children":-1},"The chi-based HTTP chassis: Config (timeouts, CORS, body limit), the shared lifecycle, and how server.Run wires a router.",{"title":267,"path":268,"stem":269,"description":2332,"icon":70,"children":-1},"A dedicated, horizontally scalable gateway that pushes notifications live to connected clients: server→client only, built on centrifuge.",1785445891374]