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