[{"data":1,"prerenderedAt":2335},["ShallowReactive",2],{"navigation_docs":3,"-docs-guides-multi-server-swarm":981},[4,27,63,138,300,324,906,915,963,977],{"title":5,"path":6,"stem":7,"children":8,"order":10,"icon":11},"Overview","\u002Fdocs\u002Foverview","docs\u002F1.overview\u002Findex",[9,12,18,24],{"title":5,"path":6,"stem":7,"order":10,"icon":11},1,"i-lucide-info",{"title":13,"path":14,"stem":15,"order":16,"icon":17},"Getting Started","\u002Fdocs\u002Foverview\u002Fgetting-started","docs\u002F1.overview\u002F2.getting-started",2,"i-lucide-rocket",{"title":19,"path":20,"stem":21,"order":22,"icon":23},"Contributing","\u002Fdocs\u002Foverview\u002Fcontributing","docs\u002F1.overview\u002F3.contributing",3,"i-lucide-heart",{"title":5,"path":25,"stem":26,"order":10},"\u002Fdocs\u002Foverview\u002F_dir","docs\u002F1.overview\u002F_dir",{"title":28,"path":29,"stem":30,"children":31,"page":62},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[32,37,42,47,53,59],{"title":33,"path":34,"stem":35,"order":10,"icon":36},"Events","\u002Fdocs\u002Fconcepts\u002Fevents","docs\u002F2.concepts\u002F1.events","i-lucide-zap",{"title":38,"path":39,"stem":40,"order":16,"icon":41},"Transports","\u002Fdocs\u002Fconcepts\u002Ftransports","docs\u002F2.concepts\u002F2.transports","i-lucide-network",{"title":43,"path":44,"stem":45,"order":22,"icon":46},"Module System","\u002Fdocs\u002Fconcepts\u002Fmodules","docs\u002F2.concepts\u002F3.modules","i-lucide-puzzle",{"title":48,"path":49,"stem":50,"order":51,"icon":52},"Flows","\u002Fdocs\u002Fconcepts\u002Fflows","docs\u002F2.concepts\u002F4.flows",4,"i-lucide-workflow",{"title":54,"path":55,"stem":56,"order":57,"icon":58},"Scheduler","\u002Fdocs\u002Fconcepts\u002Fscheduler","docs\u002F2.concepts\u002F5.scheduler",5,"i-lucide-clock",{"title":28,"path":60,"stem":61,"order":16},"\u002Fdocs\u002Fconcepts\u002F_dir","docs\u002F2.concepts\u002F_dir",false,{"title":64,"path":65,"stem":66,"children":67,"page":62},"Guides","\u002Fdocs\u002Fguides","docs\u002F3.guides",[68,73,77,81,86,90,94,98,102,107,113,119,125,131,135],{"title":69,"path":70,"stem":71,"order":10,"icon":72},"Creating a Go App","\u002Fdocs\u002Fguides\u002Fcreating-go-app","docs\u002F3.guides\u002F1.creating-go-app","i-lucide-terminal",{"title":74,"path":75,"stem":76},"E2E tests & demo videos","\u002Fdocs\u002Fguides\u002Fe2e-and-demo-videos","docs\u002F3.guides\u002F10.e2e-and-demo-videos",{"title":78,"path":79,"stem":80,"icon":17},"Deploying to production","\u002Fdocs\u002Fguides\u002Fdeploying-to-production","docs\u002F3.guides\u002F11.deploying-to-production",{"title":82,"path":83,"stem":84,"icon":85},"Events & services dashboard","\u002Fdocs\u002Fguides\u002Fevents-dashboard","docs\u002F3.guides\u002F12.events-dashboard","i-lucide-radio-tower",{"title":87,"path":88,"stem":89,"icon":41},"Multi-server Swarm","\u002Fdocs\u002Fguides\u002Fmulti-server-swarm","docs\u002F3.guides\u002F13.multi-server-swarm",{"title":91,"path":92,"stem":93,"order":16,"icon":46},"Creating a Go Module","\u002Fdocs\u002Fguides\u002Fcreating-go-module","docs\u002F3.guides\u002F2.creating-go-module",{"title":95,"path":96,"stem":97,"order":22,"icon":36},"Adding Events","\u002Fdocs\u002Fguides\u002Fadding-events-to-go-module","docs\u002F3.guides\u002F3.adding-events-to-go-module",{"title":99,"path":100,"stem":101,"order":51,"icon":52},"FlowDSL to Service","\u002Fdocs\u002Fguides\u002Fflowdsl-to-redelay","docs\u002F3.guides\u002F4.flowdsl-to-redelay",{"title":103,"path":104,"stem":105,"order":57,"icon":106},"Choosing a Transport","\u002Fdocs\u002Fguides\u002Fchoosing-a-transport","docs\u002F3.guides\u002F5.choosing-a-transport","i-lucide-git-branch",{"title":108,"path":109,"stem":110,"order":111,"icon":112},"Module Browser (\u002Fmodules)","\u002Fdocs\u002Fguides\u002Fmodules-browser","docs\u002F3.guides\u002F6.modules-browser",6,"i-lucide-layout-dashboard",{"title":114,"path":115,"stem":116,"order":117,"icon":118},"Running Flows","\u002Fdocs\u002Fguides\u002Frunning-flows","docs\u002F3.guides\u002F7.running-flows",7,"i-lucide-play-circle",{"title":120,"path":121,"stem":122,"order":123,"icon":124},"Trace + Live Observability","\u002Fdocs\u002Fguides\u002Fobservability","docs\u002F3.guides\u002F8.observability",8,"i-lucide-activity",{"title":126,"path":127,"stem":128,"order":129,"icon":130},"Project AI Assistant","\u002Fdocs\u002Fguides\u002Fai-assistant","docs\u002F3.guides\u002F9.ai-assistant",9,"i-lucide-sparkles",{"title":132,"path":133,"stem":134},"Debug taps — trace live data through any node","\u002Fdocs\u002Fguides\u002Fdebug-taps","docs\u002F3.guides\u002F9.debug-taps",{"title":64,"path":136,"stem":137,"order":22},"\u002Fdocs\u002Fguides\u002F_dir","docs\u002F3.guides\u002F_dir",{"title":139,"path":140,"stem":141,"children":142,"page":62},"Reference","\u002Fdocs\u002Freference","docs\u002F4.reference",[143,148,152,157,162,167,172,177,181,186,191,196,202,208,214,219,224,228,234,239,245,251,256,262,268,272,276,282,287,293,297],{"title":144,"path":145,"stem":146,"order":10,"icon":147},"Go Framework","\u002Fdocs\u002Freference\u002Fgo-framework","docs\u002F4.reference\u002F01.go-framework","i-simple-icons-go",{"title":149,"path":150,"stem":151,"order":16,"icon":46},"Built-in Modules","\u002Fdocs\u002Freference\u002Fgo-modules","docs\u002F4.reference\u002F02.go-modules",{"title":153,"path":154,"stem":155,"order":22,"icon":156},"Domain Addon Modules","\u002Fdocs\u002Freference\u002Fgo-modules-addons","docs\u002F4.reference\u002F03.go-modules-addons","i-lucide-blocks",{"title":158,"path":159,"stem":160,"order":51,"icon":161},"Go Events","\u002Fdocs\u002Freference\u002Fgo-events","docs\u002F4.reference\u002F04.go-events","i-lucide-radio",{"title":163,"path":164,"stem":165,"order":57,"icon":166},"Email Module","\u002Fdocs\u002Freference\u002Fgo-module-email","docs\u002F4.reference\u002F05.go-module-email","i-lucide-mail",{"title":168,"path":169,"stem":170,"order":111,"icon":171},"Go Core & IR","\u002Fdocs\u002Freference\u002Fgo-core","docs\u002F4.reference\u002F06.go-core","i-lucide-cpu",{"title":173,"path":174,"stem":175,"order":117,"icon":176},"Module Definition Files","\u002Fdocs\u002Freference\u002Fgo-module-files","docs\u002F4.reference\u002F07.go-module-files","i-lucide-file-code",{"title":178,"path":179,"stem":180,"order":123,"icon":72},"CLI Reference","\u002Fdocs\u002Freference\u002Fgo-cli","docs\u002F4.reference\u002F08.go-cli",{"title":182,"path":183,"stem":184,"order":129,"icon":185},"Go Formats","\u002Fdocs\u002Freference\u002Fgo-formats","docs\u002F4.reference\u002F09.go-formats","i-lucide-file-json",{"title":187,"path":188,"stem":189,"order":190,"icon":161},"AsyncAPI Module","\u002Fdocs\u002Freference\u002Fgo-asyncapi","docs\u002F4.reference\u002F10.go-asyncapi",10,{"title":192,"path":193,"stem":194,"order":195,"icon":52},"Go FlowDSL","\u002Fdocs\u002Freference\u002Fgo-flowdsl","docs\u002F4.reference\u002F11.go-flowdsl",11,{"title":197,"path":198,"stem":199,"order":200,"icon":201},"FlowDSL Examples","\u002Fdocs\u002Freference\u002Fflowdsl-examples","docs\u002F4.reference\u002F12.flowdsl-examples",12,"i-lucide-file-code-2",{"title":203,"path":204,"stem":205,"order":206,"icon":207},"FlowDSL Sync","\u002Fdocs\u002Freference\u002Fflowdsl-sync","docs\u002F4.reference\u002F13.flowdsl-sync",13,"i-lucide-refresh-cw",{"title":209,"path":210,"stem":211,"order":212,"icon":213},"Node.js Framework","\u002Fdocs\u002Freference\u002Fjs-framework","docs\u002F4.reference\u002F14.js-framework",14,"i-simple-icons-nodedotjs",{"title":215,"path":216,"stem":217,"order":212,"icon":218},"Python Framework","\u002Fdocs\u002Freference\u002Fpy-framework","docs\u002F4.reference\u002F14.py-framework","i-simple-icons-python",{"title":220,"path":221,"stem":222,"order":223,"icon":213},"Node.js Events","\u002Fdocs\u002Freference\u002Fjs-events","docs\u002F4.reference\u002F15.js-events",15,{"title":225,"path":226,"stem":227,"order":223,"icon":218},"Python Events","\u002Fdocs\u002Freference\u002Fpy-events","docs\u002F4.reference\u002F15.py-events",{"title":229,"path":230,"stem":231,"order":232,"icon":233},"go-ai — LLM Providers, Guard, Ledger","\u002Fdocs\u002Freference\u002Fgo-ai","docs\u002F4.reference\u002F16.go-ai",16,"i-lucide-brain-circuit",{"title":235,"path":236,"stem":237,"order":238,"icon":41},"Infrastructure Modules","\u002Fdocs\u002Freference\u002Fgeneric-infra-modules","docs\u002F4.reference\u002F17.generic-infra-modules",17,{"title":240,"path":241,"stem":242,"order":243,"icon":244},"Flow Templates","\u002Fdocs\u002Freference\u002Fflow-templates","docs\u002F4.reference\u002F18.flow-templates",18,"i-lucide-library-big",{"title":246,"path":247,"stem":248,"order":249,"icon":250},"Assistant (chat + handoff)","\u002Fdocs\u002Freference\u002Fassistant","docs\u002F4.reference\u002F19.assistant",19,"i-lucide-messages-square",{"title":252,"path":253,"stem":254,"order":255,"icon":41},"Coordination","\u002Fdocs\u002Freference\u002Fcoordination","docs\u002F4.reference\u002F20.coordination",20,{"title":257,"path":258,"stem":259,"order":260,"icon":261},"Search (vector)","\u002Fdocs\u002Freference\u002Fsearch","docs\u002F4.reference\u002F21.search",21,"i-lucide-search",{"title":263,"path":264,"stem":265,"order":266,"icon":267},"Profiles","\u002Fdocs\u002Freference\u002Fprofiles","docs\u002F4.reference\u002F22.profiles",22,"i-lucide-bookmark",{"title":269,"path":270,"stem":271},"Flow-driven HTTP endpoints","\u002Fdocs\u002Freference\u002Fflow-driven-endpoints","docs\u002F4.reference\u002F23.flow-driven-endpoints",{"title":273,"path":274,"stem":275},"Deployments","\u002Fdocs\u002Freference\u002Fdeployments","docs\u002F4.reference\u002F24.deployments",{"title":277,"path":278,"stem":279,"order":280,"icon":281},"i18n (translations)","\u002Fdocs\u002Freference\u002Fi18n","docs\u002F4.reference\u002F25.i18n",25,"i-lucide-languages",{"title":283,"path":284,"stem":285,"order":286,"icon":161},"WebSockets","\u002Fdocs\u002Freference\u002Fwebsockets","docs\u002F4.reference\u002F26.websockets",26,{"title":288,"path":289,"stem":290,"order":291,"icon":292},"Localized content","\u002Fdocs\u002Freference\u002Flocalized-content","docs\u002F4.reference\u002F27.localized-content",27,"i-lucide-file-text",{"title":294,"path":295,"stem":296},"Instagram","\u002Fdocs\u002Freference\u002Finstagram","docs\u002F4.reference\u002F28.instagram",{"title":139,"path":298,"stem":299,"order":51},"\u002Fdocs\u002Freference\u002F_dir","docs\u002F4.reference\u002F_dir",{"title":301,"path":302,"stem":303,"children":304,"page":62},"Integrations","\u002Fdocs\u002Fintegrations","docs\u002F5.integrations",[305,309,313,317,321],{"title":306,"path":307,"stem":308,"order":10,"icon":52},"FlowDSL","\u002Fdocs\u002Fintegrations\u002Fflowdsl","docs\u002F5.integrations\u002F1.flowdsl",{"title":310,"path":311,"stem":312,"order":16,"icon":292},"OpenAPI","\u002Fdocs\u002Fintegrations\u002Fopenapi","docs\u002F5.integrations\u002F2.openapi",{"title":314,"path":315,"stem":316,"order":22,"icon":161},"AsyncAPI","\u002Fdocs\u002Fintegrations\u002Fasyncapi","docs\u002F5.integrations\u002F3.asyncapi",{"title":318,"path":319,"stem":320},"MCP Integration","\u002Fdocs\u002Fintegrations\u002Fmcp","docs\u002F5.integrations\u002F4.mcp",{"title":301,"path":322,"stem":323,"order":57},"\u002Fdocs\u002Fintegrations\u002F_dir","docs\u002F5.integrations\u002F_dir",{"title":325,"path":326,"stem":327,"children":328},"FlowDSL Node Catalog","\u002Fdocs\u002Fnodes","docs\u002F5.nodes\u002Findex",[329,330,334,338,342,346,350,354,358,362,366,370,374,378,382,386,390,394,398,402,406,410,414,418,422,426,430,434,438,442,446,450,454,458,462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,530,534,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646,650,654,658,662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726,730,734,738,742,746,750,754,758,762,766,770,774,778,782,786,790,794,798,802,806,810,814,818,822,826,830,834,838,842,846,850,854,858,862,866,870,874,878,882,886,890,894,898,902],{"title":325,"path":326,"stem":327},{"title":331,"path":332,"stem":333,"order":57},"Node Catalog","\u002Fdocs\u002Fnodes\u002F_dir","docs\u002F5.nodes\u002F_dir",{"title":335,"path":336,"stem":337},"Record Alert","\u002Fdocs\u002Fnodes\u002Falerts_record","docs\u002F5.nodes\u002Falerts_record",{"title":339,"path":340,"stem":341},"Log to API Log","\u002Fdocs\u002Fnodes\u002Fapilog_record","docs\u002F5.nodes\u002Fapilog_record",{"title":343,"path":344,"stem":345},"ClickHouse Exec","\u002Fdocs\u002Fnodes\u002Fclickhouse_exec","docs\u002F5.nodes\u002Fclickhouse_exec",{"title":347,"path":348,"stem":349},"ClickHouse Insert","\u002Fdocs\u002Fnodes\u002Fclickhouse_insert","docs\u002F5.nodes\u002Fclickhouse_insert",{"title":351,"path":352,"stem":353},"ClickHouse Query","\u002Fdocs\u002Fnodes\u002Fclickhouse_query","docs\u002F5.nodes\u002Fclickhouse_query",{"title":355,"path":356,"stem":357},"Array Op","\u002Fdocs\u002Fnodes\u002Fcore_array-ops","docs\u002F5.nodes\u002Fcore_array-ops",{"title":359,"path":360,"stem":361},"Assert","\u002Fdocs\u002Fnodes\u002Fcore_assert","docs\u002F5.nodes\u002Fcore_assert",{"title":363,"path":364,"stem":365},"Audit Emit","\u002Fdocs\u002Fnodes\u002Fcore_audit-emit","docs\u002F5.nodes\u002Fcore_audit-emit",{"title":367,"path":368,"stem":369},"Catch","\u002Fdocs\u002Fnodes\u002Fcore_catch","docs\u002F5.nodes\u002Fcore_catch",{"title":371,"path":372,"stem":373},"DateTime","\u002Fdocs\u002Fnodes\u002Fcore_datetime","docs\u002F5.nodes\u002Fcore_datetime",{"title":375,"path":376,"stem":377},"Dead Letter","\u002Fdocs\u002Fnodes\u002Fcore_dead-letter","docs\u002F5.nodes\u002Fcore_dead-letter",{"title":379,"path":380,"stem":381},"Delay","\u002Fdocs\u002Fnodes\u002Fcore_delay","docs\u002F5.nodes\u002Fcore_delay",{"title":383,"path":384,"stem":385},"ForEach","\u002Fdocs\u002Fnodes\u002Fcore_foreach","docs\u002F5.nodes\u002Fcore_foreach",{"title":387,"path":388,"stem":389},"Idempotency Key","\u002Fdocs\u002Fnodes\u002Fcore_idempotency-key","docs\u002F5.nodes\u002Fcore_idempotency-key",{"title":391,"path":392,"stem":393},"If","\u002Fdocs\u002Fnodes\u002Fcore_if","docs\u002F5.nodes\u002Fcore_if",{"title":395,"path":396,"stem":397},"Inspect","\u002Fdocs\u002Fnodes\u002Fcore_inspect","docs\u002F5.nodes\u002Fcore_inspect",{"title":399,"path":400,"stem":401},"JSON Merge","\u002Fdocs\u002Fnodes\u002Fcore_json-merge","docs\u002F5.nodes\u002Fcore_json-merge",{"title":403,"path":404,"stem":405},"JSON Schema Validate","\u002Fdocs\u002Fnodes\u002Fcore_json-schema-validate","docs\u002F5.nodes\u002Fcore_json-schema-validate",{"title":407,"path":408,"stem":409},"JSONPath Extract","\u002Fdocs\u002Fnodes\u002Fcore_jsonpath-extract","docs\u002F5.nodes\u002Fcore_jsonpath-extract",{"title":411,"path":412,"stem":413},"Log","\u002Fdocs\u002Fnodes\u002Fcore_log","docs\u002F5.nodes\u002Fcore_log",{"title":415,"path":416,"stem":417},"Math","\u002Fdocs\u002Fnodes\u002Fcore_math","docs\u002F5.nodes\u002Fcore_math",{"title":419,"path":420,"stem":421},"Merge","\u002Fdocs\u002Fnodes\u002Fcore_merge","docs\u002F5.nodes\u002Fcore_merge",{"title":423,"path":424,"stem":425},"Metric Counter","\u002Fdocs\u002Fnodes\u002Fcore_metric-counter","docs\u002F5.nodes\u002Fcore_metric-counter",{"title":427,"path":428,"stem":429},"Metric Gauge","\u002Fdocs\u002Fnodes\u002Fcore_metric-gauge","docs\u002F5.nodes\u002Fcore_metric-gauge",{"title":431,"path":432,"stem":433},"Mock","\u002Fdocs\u002Fnodes\u002Fcore_mock","docs\u002F5.nodes\u002Fcore_mock",{"title":435,"path":436,"stem":437},"No-op","\u002Fdocs\u002Fnodes\u002Fcore_noop","docs\u002F5.nodes\u002Fcore_noop",{"title":439,"path":440,"stem":441},"Object Omit","\u002Fdocs\u002Fnodes\u002Fcore_object-omit","docs\u002F5.nodes\u002Fcore_object-omit",{"title":443,"path":444,"stem":445},"Object Pick","\u002Fdocs\u002Fnodes\u002Fcore_object-pick","docs\u002F5.nodes\u002Fcore_object-pick",{"title":447,"path":448,"stem":449},"Parallel Fan-Out","\u002Fdocs\u002Fnodes\u002Fcore_parallel","docs\u002F5.nodes\u002Fcore_parallel",{"title":451,"path":452,"stem":453},"Raise","\u002Fdocs\u002Fnodes\u002Fcore_raise","docs\u002F5.nodes\u002Fcore_raise",{"title":455,"path":456,"stem":457},"Rate Limit Check","\u002Fdocs\u002Fnodes\u002Fcore_rate-limit-check","docs\u002F5.nodes\u002Fcore_rate-limit-check",{"title":459,"path":460,"stem":461},"Regex Extract","\u002Fdocs\u002Fnodes\u002Fcore_regex-extract","docs\u002F5.nodes\u002Fcore_regex-extract",{"title":463,"path":464,"stem":465},"Require Fields","\u002Fdocs\u002Fnodes\u002Fcore_require-fields","docs\u002F5.nodes\u002Fcore_require-fields",{"title":467,"path":468,"stem":469},"Rethrow","\u002Fdocs\u002Fnodes\u002Fcore_rethrow","docs\u002F5.nodes\u002Fcore_rethrow",{"title":471,"path":472,"stem":473},"Return","\u002Fdocs\u002Fnodes\u002Fcore_return","docs\u002F5.nodes\u002Fcore_return",{"title":475,"path":476,"stem":477},"HMAC Signature Verify","\u002Fdocs\u002Fnodes\u002Fcore_signature-verify","docs\u002F5.nodes\u002Fcore_signature-verify",{"title":479,"path":480,"stem":481},"String Op","\u002Fdocs\u002Fnodes\u002Fcore_string-ops","docs\u002F5.nodes\u002Fcore_string-ops",{"title":483,"path":484,"stem":485},"Subflow Invoke","\u002Fdocs\u002Fnodes\u002Fcore_subflow-invoke","docs\u002F5.nodes\u002Fcore_subflow-invoke",{"title":487,"path":488,"stem":489},"Switch","\u002Fdocs\u002Fnodes\u002Fcore_switch","docs\u002F5.nodes\u002Fcore_switch",{"title":491,"path":492,"stem":493},"Template Render","\u002Fdocs\u002Fnodes\u002Fcore_template-render","docs\u002F5.nodes\u002Fcore_template-render",{"title":495,"path":496,"stem":497},"Throttle","\u002Fdocs\u002Fnodes\u002Fcore_throttle","docs\u002F5.nodes\u002Fcore_throttle",{"title":499,"path":500,"stem":501},"Timeout","\u002Fdocs\u002Fnodes\u002Fcore_timeout","docs\u002F5.nodes\u002Fcore_timeout",{"title":503,"path":504,"stem":505},"Type Cast","\u002Fdocs\u002Fnodes\u002Fcore_type-cast","docs\u002F5.nodes\u002Fcore_type-cast",{"title":507,"path":508,"stem":509},"Webhook Respond","\u002Fdocs\u002Fnodes\u002Fcore_webhook-respond","docs\u002F5.nodes\u002Fcore_webhook-respond",{"title":511,"path":512,"stem":513},"File Delete","\u002Fdocs\u002Fnodes\u002Ffilesys_delete","docs\u002F5.nodes\u002Ffilesys_delete",{"title":515,"path":516,"stem":517},"Directory List","\u002Fdocs\u002Fnodes\u002Ffilesys_list","docs\u002F5.nodes\u002Ffilesys_list",{"title":519,"path":520,"stem":521},"File Read","\u002Fdocs\u002Fnodes\u002Ffilesys_read","docs\u002F5.nodes\u002Ffilesys_read",{"title":523,"path":524,"stem":525},"File Stat","\u002Fdocs\u002Fnodes\u002Ffilesys_stat","docs\u002F5.nodes\u002Ffilesys_stat",{"title":527,"path":528,"stem":529},"File Write","\u002Fdocs\u002Fnodes\u002Ffilesys_write","docs\u002F5.nodes\u002Ffilesys_write",{"title":531,"path":532,"stem":533},"FTP Delete","\u002Fdocs\u002Fnodes\u002Fftp_delete","docs\u002F5.nodes\u002Fftp_delete",{"title":535,"path":536,"stem":537},"FTP Download","\u002Fdocs\u002Fnodes\u002Fftp_download","docs\u002F5.nodes\u002Fftp_download",{"title":539,"path":540,"stem":541},"FTP List","\u002Fdocs\u002Fnodes\u002Fftp_list","docs\u002F5.nodes\u002Fftp_list",{"title":543,"path":544,"stem":545},"FTP Upload","\u002Fdocs\u002Fnodes\u002Fftp_upload","docs\u002F5.nodes\u002Fftp_upload",{"title":547,"path":548,"stem":549},"HTTP DELETE","\u002Fdocs\u002Fnodes\u002Fhttp_delete","docs\u002F5.nodes\u002Fhttp_delete",{"title":551,"path":552,"stem":553},"HTTP GET","\u002Fdocs\u002Fnodes\u002Fhttp_get","docs\u002F5.nodes\u002Fhttp_get",{"title":555,"path":556,"stem":557},"HTTP POST","\u002Fdocs\u002Fnodes\u002Fhttp_post","docs\u002F5.nodes\u002Fhttp_post",{"title":559,"path":560,"stem":561},"HTTP PUT","\u002Fdocs\u002Fnodes\u002Fhttp_put","docs\u002F5.nodes\u002Fhttp_put",{"title":563,"path":564,"stem":565},"HTTP Request","\u002Fdocs\u002Fnodes\u002Fhttp_request","docs\u002F5.nodes\u002Fhttp_request",{"title":567,"path":568,"stem":569},"Fill Translations","\u002Fdocs\u002Fnodes\u002Fi18n_fill-translations","docs\u002F5.nodes\u002Fi18n_fill-translations",{"title":571,"path":572,"stem":573},"Resolve Locale","\u002Fdocs\u002Fnodes\u002Fi18n_resolve-locale","docs\u002F5.nodes\u002Fi18n_resolve-locale",{"title":575,"path":576,"stem":577},"Translate","\u002Fdocs\u002Fnodes\u002Fi18n_translate","docs\u002F5.nodes\u002Fi18n_translate",{"title":579,"path":580,"stem":581},"Mongo AGGREGATE","\u002Fdocs\u002Fnodes\u002Fmongoops_aggregate","docs\u002F5.nodes\u002Fmongoops_aggregate",{"title":583,"path":584,"stem":585},"Mongo DELETE","\u002Fdocs\u002Fnodes\u002Fmongoops_delete","docs\u002F5.nodes\u002Fmongoops_delete",{"title":587,"path":588,"stem":589},"Mongo FIND","\u002Fdocs\u002Fnodes\u002Fmongoops_find","docs\u002F5.nodes\u002Fmongoops_find",{"title":591,"path":592,"stem":593},"Mongo FIND ONE","\u002Fdocs\u002Fnodes\u002Fmongoops_find-one","docs\u002F5.nodes\u002Fmongoops_find-one",{"title":595,"path":596,"stem":597},"Mongo INSERT","\u002Fdocs\u002Fnodes\u002Fmongoops_insert","docs\u002F5.nodes\u002Fmongoops_insert",{"title":599,"path":600,"stem":601},"Mongo UPDATE","\u002Fdocs\u002Fnodes\u002Fmongoops_update","docs\u002F5.nodes\u002Fmongoops_update",{"title":603,"path":604,"stem":605},"Postgres COPY FROM","\u002Fdocs\u002Fnodes\u002Fpostgres_copy-from","docs\u002F5.nodes\u002Fpostgres_copy-from",{"title":607,"path":608,"stem":609},"Postgres Exec","\u002Fdocs\u002Fnodes\u002Fpostgres_exec","docs\u002F5.nodes\u002Fpostgres_exec",{"title":611,"path":612,"stem":613},"Postgres Query","\u002Fdocs\u002Fnodes\u002Fpostgres_query","docs\u002F5.nodes\u002Fpostgres_query",{"title":615,"path":616,"stem":617},"Postgres Transaction","\u002Fdocs\u002Fnodes\u002Fpostgres_transaction","docs\u002F5.nodes\u002Fpostgres_transaction",{"title":619,"path":620,"stem":621},"RabbitMQ Consume","\u002Fdocs\u002Fnodes\u002Frabbitmq_consume","docs\u002F5.nodes\u002Frabbitmq_consume",{"title":623,"path":624,"stem":625},"RabbitMQ Declare","\u002Fdocs\u002Fnodes\u002Frabbitmq_declare","docs\u002F5.nodes\u002Frabbitmq_declare",{"title":627,"path":628,"stem":629},"RabbitMQ Publish","\u002Fdocs\u002Fnodes\u002Frabbitmq_publish","docs\u002F5.nodes\u002Frabbitmq_publish",{"title":631,"path":632,"stem":633},"Auth Login","\u002Fdocs\u002Fnodes\u002Fredelay_auth-login","docs\u002F5.nodes\u002Fredelay_auth-login",{"title":635,"path":636,"stem":637},"Require Permission","\u002Fdocs\u002Fnodes\u002Fredelay_auth-require-permission","docs\u002F5.nodes\u002Fredelay_auth-require-permission",{"title":639,"path":640,"stem":641},"Validate Token","\u002Fdocs\u002Fnodes\u002Fredelay_auth-validate-token","docs\u002F5.nodes\u002Fredelay_auth-validate-token",{"title":643,"path":644,"stem":645},"Render Email Template","\u002Fdocs\u002Fnodes\u002Fredelay_email-render-template","docs\u002F5.nodes\u002Fredelay_email-render-template",{"title":647,"path":648,"stem":649},"Send Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send","docs\u002F5.nodes\u002Fredelay_email-send",{"title":651,"path":652,"stem":653},"Send Templated Email","\u002Fdocs\u002Fnodes\u002Fredelay_email-send-templated","docs\u002F5.nodes\u002Fredelay_email-send-templated",{"title":655,"path":656,"stem":657},"Event Source","\u002Fdocs\u002Fnodes\u002Fredelay_event-source","docs\u002F5.nodes\u002Fredelay_event-source",{"title":659,"path":660,"stem":661},"Create Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-create-group","docs\u002F5.nodes\u002Fredelay_groups-create-group",{"title":663,"path":664,"stem":665},"Delete Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-delete-group","docs\u002F5.nodes\u002Fredelay_groups-delete-group",{"title":667,"path":668,"stem":669},"Resolve Permissions","\u002Fdocs\u002Fnodes\u002Fredelay_groups-resolve-permissions","docs\u002F5.nodes\u002Fredelay_groups-resolve-permissions",{"title":671,"path":672,"stem":673},"Update Group","\u002Fdocs\u002Fnodes\u002Fredelay_groups-update-group","docs\u002F5.nodes\u002Fredelay_groups-update-group",{"title":675,"path":676,"stem":677},"HTTP Endpoint","\u002Fdocs\u002Fnodes\u002Fredelay_http-endpoint","docs\u002F5.nodes\u002Fredelay_http-endpoint",{"title":679,"path":680,"stem":681},"JSON Stream Source","\u002Fdocs\u002Fnodes\u002Fredelay_json-stream-source","docs\u002F5.nodes\u002Fredelay_json-stream-source",{"title":683,"path":684,"stem":685},"JSON → Event","\u002Fdocs\u002Fnodes\u002Fredelay_json-to-event","docs\u002F5.nodes\u002Fredelay_json-to-event",{"title":687,"path":688,"stem":689},"Kafka Produce","\u002Fdocs\u002Fnodes\u002Fredelay_kafka-produce","docs\u002F5.nodes\u002Fredelay_kafka-produce",{"title":691,"path":692,"stem":693},"Create Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-create","docs\u002F5.nodes\u002Fredelay_links-create",{"title":695,"path":696,"stem":697},"Delete Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-delete","docs\u002F5.nodes\u002Fredelay_links-delete",{"title":699,"path":700,"stem":701},"Resolve Short Link","\u002Fdocs\u002Fnodes\u002Fredelay_links-resolve","docs\u002F5.nodes\u002Fredelay_links-resolve",{"title":703,"path":704,"stem":705},"LLM Chat","\u002Fdocs\u002Fnodes\u002Fredelay_llm-chat","docs\u002F5.nodes\u002Fredelay_llm-chat",{"title":707,"path":708,"stem":709},"LLM Embed","\u002Fdocs\u002Fnodes\u002Fredelay_llm-embed","docs\u002F5.nodes\u002Fredelay_llm-embed",{"title":711,"path":712,"stem":713},"LLM Guard","\u002Fdocs\u002Fnodes\u002Fredelay_llm-guard","docs\u002F5.nodes\u002Fredelay_llm-guard",{"title":715,"path":716,"stem":717},"LLM Tool Router","\u002Fdocs\u002Fnodes\u002Fredelay_llm-tool-router","docs\u002F5.nodes\u002Fredelay_llm-tool-router",{"title":719,"path":720,"stem":721},"NATS Publish","\u002Fdocs\u002Fnodes\u002Fredelay_nats-publish","docs\u002F5.nodes\u002Fredelay_nats-publish",{"title":723,"path":724,"stem":725},"Mark Notification Read","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-mark-read","docs\u002F5.nodes\u002Fredelay_notifications-mark-read",{"title":727,"path":728,"stem":729},"Send Notification","\u002Fdocs\u002Fnodes\u002Fredelay_notifications-send","docs\u002F5.nodes\u002Fredelay_notifications-send",{"title":731,"path":732,"stem":733},"Redis Stream XADD","\u002Fdocs\u002Fnodes\u002Fredelay_redis-stream-publish","docs\u002F5.nodes\u002Fredelay_redis-stream-publish",{"title":735,"path":736,"stem":737},"S3 Copy Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-copy-object","docs\u002F5.nodes\u002Fredelay_s3-copy-object",{"title":739,"path":740,"stem":741},"S3 Delete Many","\u002Fdocs\u002Fnodes\u002Fredelay_s3-delete-many","docs\u002F5.nodes\u002Fredelay_s3-delete-many",{"title":743,"path":744,"stem":745},"S3 Head Object","\u002Fdocs\u002Fnodes\u002Fredelay_s3-head-object","docs\u002F5.nodes\u002Fredelay_s3-head-object",{"title":747,"path":748,"stem":749},"S3 List Objects","\u002Fdocs\u002Fnodes\u002Fredelay_s3-list-objects","docs\u002F5.nodes\u002Fredelay_s3-list-objects",{"title":751,"path":752,"stem":753},"Create Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-create-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-create-schedule",{"title":755,"path":756,"stem":757},"Cron Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-cron-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-cron-trigger",{"title":759,"path":760,"stem":761},"Delete Schedule","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-delete-schedule","docs\u002F5.nodes\u002Fredelay_scheduler-delete-schedule",{"title":763,"path":764,"stem":765},"Interval Trigger","\u002Fdocs\u002Fnodes\u002Fredelay_scheduler-interval-trigger","docs\u002F5.nodes\u002Fredelay_scheduler-interval-trigger",{"title":767,"path":768,"stem":769},"Search Query","\u002Fdocs\u002Fnodes\u002Fredelay_search-query","docs\u002F5.nodes\u002Fredelay_search-query",{"title":771,"path":772,"stem":773},"Search Upsert","\u002Fdocs\u002Fnodes\u002Fredelay_search-upsert","docs\u002F5.nodes\u002Fredelay_search-upsert",{"title":775,"path":776,"stem":777},"Delete Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-delete","docs\u002F5.nodes\u002Fredelay_settings-delete",{"title":779,"path":780,"stem":781},"Get Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-get","docs\u002F5.nodes\u002Fredelay_settings-get",{"title":783,"path":784,"stem":785},"Set Setting","\u002Fdocs\u002Fnodes\u002Fredelay_settings-set","docs\u002F5.nodes\u002Fredelay_settings-set",{"title":787,"path":788,"stem":789},"Delete File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-delete","docs\u002F5.nodes\u002Fredelay_storage-delete",{"title":791,"path":792,"stem":793},"Download File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-download","docs\u002F5.nodes\u002Fredelay_storage-download",{"title":795,"path":796,"stem":797},"Generate Presigned URL","\u002Fdocs\u002Fnodes\u002Fredelay_storage-presign-url","docs\u002F5.nodes\u002Fredelay_storage-presign-url",{"title":799,"path":800,"stem":801},"Upload File","\u002Fdocs\u002Fnodes\u002Fredelay_storage-upload","docs\u002F5.nodes\u002Fredelay_storage-upload",{"title":803,"path":804,"stem":805},"Subworkflow","\u002Fdocs\u002Fnodes\u002Fredelay_subworkflow","docs\u002F5.nodes\u002Fredelay_subworkflow",{"title":807,"path":808,"stem":809},"Activate User","\u002Fdocs\u002Fnodes\u002Fredelay_users-activate","docs\u002F5.nodes\u002Fredelay_users-activate",{"title":811,"path":812,"stem":813},"Create Pending User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-pending","docs\u002F5.nodes\u002Fredelay_users-create-pending",{"title":815,"path":816,"stem":817},"Create User","\u002Fdocs\u002Fnodes\u002Fredelay_users-create-user","docs\u002F5.nodes\u002Fredelay_users-create-user",{"title":819,"path":820,"stem":821},"Delete User","\u002Fdocs\u002Fnodes\u002Fredelay_users-delete-user","docs\u002F5.nodes\u002Fredelay_users-delete-user",{"title":823,"path":824,"stem":825},"Find User By Email","\u002Fdocs\u002Fnodes\u002Fredelay_users-find-by-email","docs\u002F5.nodes\u002Fredelay_users-find-by-email",{"title":827,"path":828,"stem":829},"Get User","\u002Fdocs\u002Fnodes\u002Fredelay_users-get-user","docs\u002F5.nodes\u002Fredelay_users-get-user",{"title":831,"path":832,"stem":833},"List Users","\u002Fdocs\u002Fnodes\u002Fredelay_users-list-users","docs\u002F5.nodes\u002Fredelay_users-list-users",{"title":835,"path":836,"stem":837},"Update User","\u002Fdocs\u002Fnodes\u002Fredelay_users-update-user","docs\u002F5.nodes\u002Fredelay_users-update-user",{"title":839,"path":840,"stem":841},"Request Email Verification (legacy)","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request","docs\u002F5.nodes\u002Fredelay_verification-request",{"title":843,"path":844,"stem":845},"Request Email Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-email","docs\u002F5.nodes\u002Fredelay_verification-request-email",{"title":847,"path":848,"stem":849},"Request SMS Verification","\u002Fdocs\u002Fnodes\u002Fredelay_verification-request-sms","docs\u002F5.nodes\u002Fredelay_verification-request-sms",{"title":851,"path":852,"stem":853},"Verify Token","\u002Fdocs\u002Fnodes\u002Fredelay_verification-verify","docs\u002F5.nodes\u002Fredelay_verification-verify",{"title":855,"path":856,"stem":857},"Redis DEL","\u002Fdocs\u002Fnodes\u002Fredisops_del","docs\u002F5.nodes\u002Fredisops_del",{"title":859,"path":860,"stem":861},"Redis EXPIRE","\u002Fdocs\u002Fnodes\u002Fredisops_expire","docs\u002F5.nodes\u002Fredisops_expire",{"title":863,"path":864,"stem":865},"Redis GET","\u002Fdocs\u002Fnodes\u002Fredisops_get","docs\u002F5.nodes\u002Fredisops_get",{"title":867,"path":868,"stem":869},"Redis HGET","\u002Fdocs\u002Fnodes\u002Fredisops_hget","docs\u002F5.nodes\u002Fredisops_hget",{"title":871,"path":872,"stem":873},"Redis HSET","\u002Fdocs\u002Fnodes\u002Fredisops_hset","docs\u002F5.nodes\u002Fredisops_hset",{"title":875,"path":876,"stem":877},"Redis INCR","\u002Fdocs\u002Fnodes\u002Fredisops_incr","docs\u002F5.nodes\u002Fredisops_incr",{"title":879,"path":880,"stem":881},"Redis LPOP","\u002Fdocs\u002Fnodes\u002Fredisops_lpop","docs\u002F5.nodes\u002Fredisops_lpop",{"title":883,"path":884,"stem":885},"Redis LPUSH","\u002Fdocs\u002Fnodes\u002Fredisops_lpush","docs\u002F5.nodes\u002Fredisops_lpush",{"title":887,"path":888,"stem":889},"Redis PUBLISH","\u002Fdocs\u002Fnodes\u002Fredisops_publish","docs\u002F5.nodes\u002Fredisops_publish",{"title":891,"path":892,"stem":893},"Redis SET","\u002Fdocs\u002Fnodes\u002Fredisops_set","docs\u002F5.nodes\u002Fredisops_set",{"title":895,"path":896,"stem":897},"SFTP Download","\u002Fdocs\u002Fnodes\u002Fsftp_download","docs\u002F5.nodes\u002Fsftp_download",{"title":899,"path":900,"stem":901},"SFTP Upload","\u002Fdocs\u002Fnodes\u002Fsftp_upload","docs\u002F5.nodes\u002Fsftp_upload",{"title":903,"path":904,"stem":905},"Send SMS","\u002Fdocs\u002Fnodes\u002Fsms_send","docs\u002F5.nodes\u002Fsms_send",{"title":907,"path":908,"stem":909,"children":910,"page":62},"Rfcs","\u002Fdocs\u002Frfcs","docs\u002F6.rfcs",[911],{"title":912,"path":913,"stem":914},"RFC 0001 — Worker Assignment for Flow Deployments","\u002Fdocs\u002Frfcs\u002F0001-worker-assignment","docs\u002F6.rfcs\u002F0001-worker-assignment",{"title":916,"path":917,"stem":918,"children":919,"page":62},"Admin","\u002Fdocs\u002Fadmin","docs\u002F7.admin",[920,925,930,935,940,945,950,954,959],{"title":5,"path":921,"stem":922,"order":923,"icon":924},"\u002Fdocs\u002Fadmin\u002Foverview","docs\u002F7.admin\u002F00.overview",0,"i-lucide-compass",{"title":926,"path":927,"stem":928,"order":10,"icon":929},"Getting started","\u002Fdocs\u002Fadmin\u002Fgetting-started","docs\u002F7.admin\u002F01.getting-started","i-lucide-play",{"title":931,"path":932,"stem":933,"order":16,"icon":934},"Layered architecture","\u002Fdocs\u002Fadmin\u002Flayered-architecture","docs\u002F7.admin\u002F02.layered-architecture","i-lucide-layers",{"title":936,"path":937,"stem":938,"order":22,"icon":939},"Building a custom admin project","\u002Fdocs\u002Fadmin\u002Fbuilding-a-custom-admin-project","docs\u002F7.admin\u002F03.building-a-custom-admin-project","i-lucide-folder-plus",{"title":941,"path":942,"stem":943,"order":51,"icon":944},"Building admin modules","\u002Fdocs\u002Fadmin\u002Fbuilding-admin-modules","docs\u002F7.admin\u002F04.building-admin-modules","i-lucide-package-plus",{"title":946,"path":947,"stem":948,"order":57,"icon":949},"Base module reference","\u002Fdocs\u002Fadmin\u002Freference-base-modules","docs\u002F7.admin\u002F05.reference-base-modules","i-lucide-boxes",{"title":951,"path":952,"stem":953,"order":111,"icon":17},"Deployment","\u002Fdocs\u002Fadmin\u002Fdeployment","docs\u002F7.admin\u002F06.deployment",{"title":955,"path":956,"stem":957,"order":117,"icon":958},"Consuming the base layer (versioned)","\u002Fdocs\u002Fadmin\u002Fconsuming-the-base-layer","docs\u002F7.admin\u002F07.consuming-the-base-layer","i-lucide-package",{"title":960,"path":961,"stem":962,"order":117},"Admin UI","\u002Fdocs\u002Fadmin\u002F_dir","docs\u002F7.admin\u002F_dir",{"title":964,"path":965,"stem":966,"children":967,"page":62},"Services","\u002Fdocs\u002Fservices","docs\u002F8.services",[968,973],{"title":969,"path":970,"stem":971,"order":10,"icon":972},"Custom development & support services","\u002Fdocs\u002Fservices\u002Fcustom-development","docs\u002F8.services\u002F1.custom-development","i-lucide-briefcase",{"title":964,"path":974,"stem":975,"icon":972,"order":976},"\u002Fdocs\u002Fservices\u002F_dir","docs\u002F8.services\u002F_dir",80,{"title":978,"path":979,"stem":980,"order":923},"Dir","\u002Fdocs\u002F_dir","docs\u002F_dir",{"id":982,"title":87,"body":983,"description":2328,"extension":2329,"links":2330,"meta":2331,"navigation":2332,"path":88,"seo":2333,"stem":89,"__hash__":2334},"docs\u002Fdocs\u002F3.guides\u002F13.multi-server-swarm.md",{"type":984,"value":985,"toc":2313},"minimark",[986,990,994,1006,1011,1127,1140,1144,1151,1154,1186,1268,1273,1319,1324,1480,1484,1495,1498,1563,1576,1580,1587,1669,1675,1680,1793,1797,1804,1842,1902,1905,1909,1953,1957,1973,1977,1980,1997,2113,2120,2124,2226,2230,2309],[987,988,87],"h1",{"id":989},"multi-server-swarm",[991,992,993],"p",{},"A Redelay project scales out by running its app tier on several Swarm nodes —\noften in different regions — next to replicated data services. That only pays\noff if every hop on a request's path stays local. This guide is the checklist.",[991,995,996,1000,1001,1005],{},[997,998,999],"strong",{},"The one number to keep in mind:"," a request makes several ",[1002,1003,1004],"em",{},"sequential"," data\ncalls, so cross-region round trips multiply. A replica 147 ms away from the\ndatabase primary turned a 59 ms p50 list call into 740 ms — five reads, each\ncrossing the ocean. Measure the RTT between your nodes before placing anything.",[1007,1008,1010],"h2",{"id":1009},"_1-what-is-local-what-is-not","1. What is local, what is not",[1012,1013,1014,1030],"table",{},[1015,1016,1017],"thead",{},[1018,1019,1020,1024,1027],"tr",{},[1021,1022,1023],"th",{},"Service",[1021,1025,1026],{},"How a replica connects",[1021,1028,1029],{},"Local to the task?",[1031,1032,1033,1048,1061,1078,1102,1116],"tbody",{},[1018,1034,1035,1039,1045],{},[1036,1037,1038],"td",{},"ClickHouse (replicated tables)",[1036,1040,1041],{},[1042,1043,1044],"code",{},"CLICKHOUSE_HOST=clickhouse-{{.Node.Hostname}}",[1036,1046,1047],{},"✅ reads and inserts hit the node's own replica; replication is async (seconds)",[1018,1049,1050,1053,1058],{},[1036,1051,1052],{},"Redis — cache",[1036,1054,1055],{},[1042,1056,1057],{},"REDIS_URL=redis:\u002F\u002Fredis-{{.Node.Hostname}}:6379\u002F0",[1036,1059,1060],{},"✅ per-node cache; invalidate across nodes with broadcast events",[1018,1062,1063,1066,1075],{},[1036,1064,1065],{},"Redis — shared (locks, rate limits, coordination)",[1036,1067,1068,1071,1072],{},[1042,1069,1070],{},"COORD=redis",", ",[1042,1073,1074],{},"REDIS_SHARED_CLUSTER_NODES=redis-a:6380,redis-b:6380,…",[1036,1076,1077],{},"❌ by design: keys are hashed across the cluster, so most calls leave the node",[1018,1079,1080,1083,1088],{},[1036,1081,1082],{},"MongoDB replica set",[1036,1084,1085],{},[1042,1086,1087],{},"MONGO_URI=mongodb:\u002F\u002F…@mongo-a,mongo-b,mongo-c\u002F?replicaSet=…",[1036,1089,1090,1091,1094,1095,1098,1099],{},"❌ ",[997,1092,1093],{},"reads go to the primary"," unless a read opts into ",[1042,1096,1097],{},"Nearest","; ",[997,1100,1101],{},"writes always go to the primary",[1018,1103,1104,1107,1113],{},[1036,1105,1106],{},"Kafka",[1036,1108,1109,1112],{},[1042,1110,1111],{},"KAFKA_BOOTSTRAP_SERVERS="," every broker",[1036,1114,1115],{},"❌ producers write to each partition's leader — keep publishing off the request path",[1018,1117,1118,1121,1124],{},[1036,1119,1120],{},"Object storage \u002F vector DB \u002F LLM",[1036,1122,1123],{},"single endpoint",[1036,1125,1126],{},"❌ treat as remote",[991,1128,1129,1132,1133,1071,1136,1139],{},[1042,1130,1131],{},"{{.Node.Hostname}}"," is a Swarm template: the task's env var is rendered with\nthe node it lands on, so one stack file gives every replica its local member.\nName the per-node services to match (",[1042,1134,1135],{},"clickhouse-\u003Chostname>",[1042,1137,1138],{},"redis-\u003Chostname>",").",[1007,1141,1143],{"id":1142},"_2-mongodb-primary-by-default-nearest-per-read","2. MongoDB: primary by default, Nearest per read",[991,1145,1146,1147,1150],{},"The client default stays ",[1042,1148,1149],{},"MONGO_READ_PREFERENCE=primary"," — never flip it\nglobally. Replication is asynchronous: a blanket secondary preference breaks\nread-your-writes for every endpoint at once (the user saves something, the next\nrequest lands on a lagging member, and it seems to vanish).",[991,1152,1153],{},"Opt in per read, where staleness is acceptable:",[1155,1156,1161],"pre",{"className":1157,"code":1158,"language":1159,"meta":1160,"style":1160},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import rtmongo \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\"\n\nctx = rtmongo.Nearest(ctx) \u002F\u002F catalogue, browse, public lists — not what the user just wrote\nitems, err := svc.store.List(ctx, params)\n","go","",[1042,1162,1163,1170,1176,1181],{"__ignoreMap":1160},[1164,1165,1167],"span",{"class":1166,"line":10},"line",[1164,1168,1169],{},"import rtmongo \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\"\n",[1164,1171,1172],{"class":1166,"line":16},[1164,1173,1175],{"emptyLinePlaceholder":1174},true,"\n",[1164,1177,1178],{"class":1166,"line":22},[1164,1179,1180],{},"ctx = rtmongo.Nearest(ctx) \u002F\u002F catalogue, browse, public lists — not what the user just wrote\n",[1164,1182,1183],{"class":1166,"line":51},[1164,1184,1185],{},"items, err := svc.store.List(ctx, params)\n",[1187,1188,1189,1234,1259,1265],"ul",{},[1190,1191,1192,1195,1196,1199,1200,1071,1203,1071,1206,1071,1209,1071,1212,1071,1215,1071,1218,1221,1222,1225,1226,1228,1229,1228,1231,1233],"li",{},[1042,1193,1194],{},"crud.MongoCRUD"," honours the context preference on ",[997,1197,1198],{},"every"," read\n(",[1042,1201,1202],{},"FindByID",[1042,1204,1205],{},"FindByField",[1042,1207,1208],{},"FindOne",[1042,1210,1211],{},"Exists",[1042,1213,1214],{},"List",[1042,1216,1217],{},"ListAll",[1042,1219,1220],{},"Count",")\nfrom go-framework ",[997,1223,1224],{},"v0.16.6","; before that only ",[1042,1227,1214],{},"\u002F",[1042,1230,1217],{},[1042,1232,1220],{}," did.",[1190,1235,1236,1237,1240,1241,1246,1247,1250,1251,1254,1255,1258],{},"A raw ",[1042,1238,1239],{},"*mongo.Collection"," ignores the context. Wrap it with\n",[997,1242,1243],{},[1042,1244,1245],{},"rtmongo.Coll(ctx, coll)"," (go-framework ",[997,1248,1249],{},"v0.17.0","), which applies the\ncontext's preference through a cached clone:\n",[1042,1252,1253],{},"rtmongo.Coll(rtmongo.Nearest(ctx), coll).Find(ctx, filter)",". Most hot paths\nuse raw collections, and before v0.17.0 ",[1042,1256,1257],{},"Nearest(ctx)"," silently did nothing for them.",[1190,1260,1261,1264],{},[1042,1262,1263],{},"rtmongo.Secondary(ctx)"," (SecondaryPreferred) is for reports and exports.",[1190,1266,1267],{},"The framework's auth middleware is JWT-only — no database read per request.",[991,1269,1270],{},[997,1271,1272],{},"Classify every read on a hot path:",[1012,1274,1275,1285],{},[1015,1276,1277],{},[1018,1278,1279,1282],{},[1021,1280,1281],{},"Read",[1021,1283,1284],{},"Preference",[1031,1286,1287,1296,1304,1312],{},[1018,1288,1289,1292],{},[1036,1290,1291],{},"Catalogue, browse, public data, configuration, other users' public data",[1036,1293,1294],{},[1042,1295,1097],{},[1018,1297,1298,1301],{},[1036,1299,1300],{},"The user's own data they may have just written (favourites, lists, prefs, read-state)",[1036,1302,1303],{},"primary — or one of the read-your-writes patterns below",[1018,1305,1306,1309],{},[1036,1307,1308],{},"Anything a write decision depends on (quota, billing, uniqueness, idempotency)",[1036,1310,1311],{},"primary",[1018,1313,1314,1317],{},[1036,1315,1316],{},"Login, refresh, signup, password reset",[1036,1318,1311],{},[1320,1321,1323],"h3",{"id":1322},"read-your-writes-on-a-nearby-replica","Read-your-writes on a nearby replica",[1325,1326,1327,1455],"ol",{},[1190,1328,1329,1332,1333,1336,1337,1339,1340,1343,1344,1411],{},[997,1330,1331],{},"Recent-write window (simple, built in since go-framework v0.17.0)."," A\nwrite by user ",[1002,1334,1335],{},"U"," marks ",[1002,1338,1335],{}," in shared Redis for ",[1042,1341,1342],{},"RecentWindow"," (10 s); U's\nown reads go to the primary while the mark exists, and to the nearest member\notherwise.",[1155,1345,1347],{"className":1157,"code":1346,"language":1159,"meta":1160,"style":1160},"import (\n    rtmongo \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\"\n    \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\u002Frecentwrite\"\n)\n\n\u002F\u002F at startup: the marker on the shared Redis Cluster; this node's master host\nrtmongo.SetMarker(recentwrite.New(clusterClient, \"myapp:shared:\", \"redis-fp-\"+nodeHostname))\n\n\u002F\u002F after authentication (or rtmongo.RecentWriteMiddleware(userIDFromRequest)):\nctx = rtmongo.Request(ctx, userID, r.Method) \u002F\u002F a write marks U before it runs\n\n\u002F\u002F a read of U's own data:\ncur, err := rtmongo.Coll(rtmongo.ForUser(ctx, userID), favourites).Find(ctx, filter)\n",[1042,1348,1349,1354,1359,1364,1369,1373,1378,1383,1387,1392,1397,1401,1406],{"__ignoreMap":1160},[1164,1350,1351],{"class":1166,"line":10},[1164,1352,1353],{},"import (\n",[1164,1355,1356],{"class":1166,"line":16},[1164,1357,1358],{},"    rtmongo \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\"\n",[1164,1360,1361],{"class":1166,"line":22},[1164,1362,1363],{},"    \"github.com\u002Fredelay\u002Fgo-framework\u002Fruntime\u002Fmongo\u002Frecentwrite\"\n",[1164,1365,1366],{"class":1166,"line":51},[1164,1367,1368],{},")\n",[1164,1370,1371],{"class":1166,"line":57},[1164,1372,1175],{"emptyLinePlaceholder":1174},[1164,1374,1375],{"class":1166,"line":111},[1164,1376,1377],{},"\u002F\u002F at startup: the marker on the shared Redis Cluster; this node's master host\n",[1164,1379,1380],{"class":1166,"line":117},[1164,1381,1382],{},"rtmongo.SetMarker(recentwrite.New(clusterClient, \"myapp:shared:\", \"redis-fp-\"+nodeHostname))\n",[1164,1384,1385],{"class":1166,"line":123},[1164,1386,1175],{"emptyLinePlaceholder":1174},[1164,1388,1389],{"class":1166,"line":129},[1164,1390,1391],{},"\u002F\u002F after authentication (or rtmongo.RecentWriteMiddleware(userIDFromRequest)):\n",[1164,1393,1394],{"class":1166,"line":190},[1164,1395,1396],{},"ctx = rtmongo.Request(ctx, userID, r.Method) \u002F\u002F a write marks U before it runs\n",[1164,1398,1399],{"class":1166,"line":195},[1164,1400,1175],{"emptyLinePlaceholder":1174},[1164,1402,1403],{"class":1166,"line":200},[1164,1404,1405],{},"\u002F\u002F a read of U's own data:\n",[1164,1407,1408],{"class":1166,"line":206},[1164,1409,1410],{},"cur, err := rtmongo.Coll(rtmongo.ForUser(ctx, userID), favourites).Find(ctx, filter)\n",[1187,1412,1413,1419,1429,1442,1448],{},[1190,1414,1415,1418],{},[997,1416,1417],{},"Mark before the write runs",", not after: a read racing the write, and\nthe write's own read-back, must go to the primary.",[1190,1420,1421,1424,1425,1428],{},[997,1422,1423],{},"Ask once per request",", and only if a user-owned read happens.\n",[1042,1426,1427],{},"Request"," memoises it; a hot path that checks per read pays one Redis\nround trip per read.",[1190,1430,1431,1434,1435,1438,1439,1139],{},[997,1432,1433],{},"Keep the check local."," A Redis Cluster without replicas keeps a key on\none master, maybe in another region, and a 147 ms check cancels the\nNearest read it decides. ",[1042,1436,1437],{},"recentwrite"," writes the mark to every master\n(hash tags, in parallel: one round trip, writes only). The check reads\nthe node's own master (",[1042,1440,1441],{},"REDIS_SHARED_LOCAL_NODE=redis-fp-{{.Node.Hostname}}",[1190,1443,1444,1447],{},[997,1445,1446],{},"Fail to the primary:"," Redis down, or no marker set, means user-owned\nreads stay on the primary. That is slower, never stale.",[1190,1449,1450,1451,1454],{},"Login \u002F refresh \u002F signup carry no user before they run: they read the\nprimary explicitly, not through ",[1042,1452,1453],{},"ForUser",".",[1190,1456,1457,1460,1461,1464,1465,1468,1469,1472,1473,1475,1476,1479],{},[997,1458,1459],{},"Causal consistency (exact)."," Writes use ",[1042,1462,1463],{},"w: majority","; the response\ncarries the operation time, which the client (or a per-user Redis key)\nkeeps. The next request starts a causally consistent session,\n",[1042,1466,1467],{},"AdvanceOperationTime"," \u002F ",[1042,1470,1471],{},"AdvanceClusterTime"," to that point, and reads\n",[1042,1474,1097],{}," with ",[1042,1477,1478],{},"readConcern: majority"," — the nearby member waits until it has\nreplicated the user's write, so it can never serve something older.",[1007,1481,1483],{"id":1482},"_3-request-routing-edge-traefik-task","3. Request routing: edge → Traefik → task",[991,1485,1486,1487,1490,1491,1494],{},"With proxied DNS (e.g. Cloudflare) holding one A record per node, visitors are\nspread across nodes ",[997,1488,1489],{},"without regard to location",". Traefik runs on every node\n(global, host ports) and load-balances each service across ",[997,1492,1493],{},"all"," of its tasks\nSwarm-wide. So a request can enter in one region and be served in another, and\nthere is no \"prefer the local task\" setting in Traefik's Swarm provider.",[991,1496,1497],{},"Pick one model per project:",[1012,1499,1500,1513],{},[1015,1501,1502],{},[1018,1503,1504,1507,1510],{},[1021,1505,1506],{},"Model",[1021,1508,1509],{},"How",[1021,1511,1512],{},"When",[1031,1514,1515,1532],{},[1018,1516,1517,1522,1529],{},[1036,1518,1519],{},[997,1520,1521],{},"A. App tier next to the primary",[1036,1523,1524,1525,1528],{},"Constrain the app services to the region of the database primary (",[1042,1526,1527],{},"node.labels.location == \u003Cprimary region>","); data replicas elsewhere are for failover and for analytics reads.",[1036,1530,1531],{},"Default. One region of users, or until latency matters elsewhere.",[1018,1533,1534,1539,1560],{},[1036,1535,1536],{},[997,1537,1538],{},"B. Regional app tiers",[1036,1540,1541,1542,1071,1545,1548,1549,1552,1553,1556,1557,1559],{},"One service per region (",[1042,1543,1544],{},"site-api-eu",[1042,1546,1547],{},"site-api-us",") constrained by ",[1042,1550,1551],{},"node.labels.location",", each behind its own Traefik router on a regional host (",[1042,1554,1555],{},"eu-origin.example.com","). At the edge, a geo-steering load balancer (e.g. Cloudflare Load Balancing pools per region, origin Host header override, edge session affinity) sends each visitor to its region. Reads use ",[1042,1558,1097],{}," + one read-your-writes pattern from §2.",[1036,1561,1562],{},"Users on several continents; the data tier is already replicated per region.",[991,1564,1565,1566,1569,1570,1572,1573,1575],{},"Model B needs both halves: regional routing ",[1002,1567,1568],{},"and"," ",[1042,1571,1097],{}," reads. Regional\ntasks that still read the remote primary are just as slow; ",[1042,1574,1097],{}," reads\nbehind round-robin DNS still cross regions at the edge.",[1320,1577,1579],{"id":1578},"client-chosen-region-alternative-to-an-edge-load-balancer","Client-chosen region (alternative to an edge load balancer)",[991,1581,1582,1583,1586],{},"Free, and it picks by each user's ",[1002,1584,1585],{},"measured"," latency rather than geography:",[1325,1588,1589,1624,1637,1647,1653,1663],{},[1190,1590,1591,1594,1595,1598,1599,1602,1603,1606,1607,1071,1610,1071,1613,1616,1617,1620,1621,1623],{},[997,1592,1593],{},"One hostname per region",", each reaching only that region's tasks: a proxied\nA record per region (",[1042,1596,1597],{},"eu.example.com"," → EU nodes, ",[1042,1600,1601],{},"na.example.com"," → NA\nnode) and ",[997,1604,1605],{},"one Swarm service per region"," (",[1042,1608,1609],{},"web-eu",[1042,1611,1612],{},"web-na",[1042,1614,1615],{},"api-eu",",\n",[1042,1618,1619],{},"api-na",", constrained by ",[1042,1622,1551],{},"), each behind a router on\nits regional host. With a single service, Traefik would still balance across\nevery region's tasks.",[1190,1625,1626,1606,1629,1632,1633,1636],{},[997,1627,1628],{},"Regional hosts serve only the API path",[1042,1630,1631],{},"Host(na.example.com) && PathPrefix(\u002Fapi\u002F)","), with ",[1042,1634,1635],{},"X-Robots-Tag: noindex",". HTML and SEO stay on the\ncanonical host.",[1190,1638,1639,1642,1643,1646],{},[997,1640,1641],{},"The client chooses:"," after hydration (or at native app start) it calls a\ntiny uncached ",[1042,1644,1645],{},"GET \u002Fapi\u002F...\u002Fregion"," on every regional host in parallel, keeps\nthe fastest (stored with a TTL, re-measured in the background), sends API\ncalls there, and on a network error or 5xx switches to the next region\n(retrying idempotent requests only).",[1190,1648,1649,1652],{},[997,1650,1651],{},"Call the regional proxy, not the Go API",", when the web app talks through a\nNitro proxy or BFF that adds headers, locale or credentials. The proxy in each\nregion calls its own region's API service over the overlay.",[1190,1654,1655,1658,1659,1662],{},[997,1656,1657],{},"Cross-origin plumbing:"," CORS on the regional hosts for the canonical\norigin (with credentials if cookies are used); session cookies scoped to the\nparent domain (",[1042,1660,1661],{},"Domain=.example.com",") or bearer tokens; per-IP limits and\nsessions in shared Redis, so they don't split per region.",[1190,1664,1665,1668],{},[997,1666,1667],{},"The HTML"," still comes from the canonical host, so keep its origins in the\nprimary's region (§3 model A). Server-side rendering there stays local; only\nthe browser's API calls go regional.",[991,1670,1671,1672,1674],{},"It only pays off when the regional API reads ",[1042,1673,1097],{}," with a read-your-writes\npattern (§2). The client sticking to one region also gives session-affinity\nconsistency.",[1676,1677,1679],"h4",{"id":1678},"lessons-from-a-bff-rollout-nameniac-2026-10-15","Lessons from a BFF rollout (Nameniac, 2026-10-15)",[1187,1681,1682,1712,1718,1731,1753,1766,1772,1787],{},[1190,1683,1684,1690],{},[997,1685,1686,1687,1689],{},"Moving session cookies to ",[1042,1688,1661],{}," leaves old host-only\ncopies behind.",[1187,1691,1692,1699,1702,1705],{},[1190,1693,1694,1695,1698],{},"A browser holding both sends both. With equal paths the older one comes\nfirst, and most cookie parsers keep the ",[1002,1696,1697],{},"first"," value, so a stale refresh\ntoken wins.",[1190,1700,1701],{},"Every write from the canonical host must delete the host-only copy too.\nOnly that host can.",[1190,1703,1704],{},"Migrate once with a marker cookie.",[1190,1706,1707,1708,1711],{},"Repair browsers that already have duplicates: when a name appears twice,\nkeep the ",[997,1709,1710],{},"last"," value (the newest) and delete the host-only copy.",[1190,1713,1714,1717],{},[997,1715,1716],{},"Delete before you set."," A response that sets the domain cookie and then\ndeletes the host-only one is fine for browsers, which key by name +\ndomain. A client keyed by name alone (a synthetic probe, a script) applies\nthe delete last and loses the session.",[1190,1719,1720,1723,1724,1727,1728,1454],{},[997,1721,1722],{},"Server-render calls must forward the page's host."," An in-process SSR call\nthat refreshes the session computes the cookie Domain from its own Host\n(",[1042,1725,1726],{},"localhost",") and sets host-only cookies again. Forward ",[1042,1729,1730],{},"X-Forwarded-Host",[1190,1732,1733,1740,1741,1744,1745,1748,1749,1752],{},[997,1734,1735,1736,1739],{},"CSRF: ",[1042,1737,1738],{},"Sec-Fetch-Site: same-site"," is not enough."," Every\n",[1042,1742,1743],{},"*.example.com"," is same-site (old., admin., api.). Accept ",[1042,1746,1747],{},"same-site"," only\nwith an ",[1042,1750,1751],{},"Origin"," that is exactly the canonical origin(s).",[1190,1754,1755,1758,1759,1762,1763,1454],{},[997,1756,1757],{},"CORS preflights must be answered before your guards."," A CSRF or\nmembers-only check refuses a bodyless ",[1042,1760,1761],{},"OPTIONS",". Answer exact origins only,\nwith credentials and ",[1042,1764,1765],{},"Vary: Origin",[1190,1767,1768,1771],{},[997,1769,1770],{},"A new route on a catch-all redirect host"," (e.g. \"unknown paths → the old\nsite\") gets redirected away until it is on the own-path list. Test the\nregional routes through the real chain.",[1190,1773,1774,1777,1778,1780,1781,1071,1783,1786],{},[997,1775,1776],{},"Pre-production gates:"," a header-routed candidate can't see browser\npreflights (an ",[1042,1779,1761],{}," carries no custom header). Cover the regional hosts\nat the HTTP level (explicit ",[1042,1782,1751],{},[1042,1784,1785],{},"Sec-Fetch-Site"," and cookies), plus a\nrouter for the candidate on each regional host.",[1190,1788,1789,1792],{},[997,1790,1791],{},"Deploy checks must not assume placement."," \"Exec into a task on this node\"\nfails the day the scheduler moves it. Attachable overlays are rare; ask from\na service pinned to the runner's node.",[1007,1794,1796],{"id":1795},"_4-sticky-sessions-what-they-do-and-dont-fix","4. Sticky sessions — what they do and don't fix",[991,1798,1799,1800,1803],{},"Traefik sticky cookies pin a browser to one ",[997,1801,1802],{},"task",":",[1155,1805,1809],{"className":1806,"code":1807,"language":1808,"meta":1160,"style":1160},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","- traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie=true\n- traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie.name=\u003Csvc>_srv\n- traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie.secure=true\n- traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie.httponly=true\n","yaml",[1042,1810,1811,1821,1828,1835],{"__ignoreMap":1160},[1164,1812,1813,1817],{"class":1166,"line":10},[1164,1814,1816],{"class":1815},"sMK4o","-",[1164,1818,1820],{"class":1819},"sfazB"," traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie=true\n",[1164,1822,1823,1825],{"class":1166,"line":16},[1164,1824,1816],{"class":1815},[1164,1826,1827],{"class":1819}," traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie.name=\u003Csvc>_srv\n",[1164,1829,1830,1832],{"class":1166,"line":22},[1164,1831,1816],{"class":1815},[1164,1833,1834],{"class":1819}," traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie.secure=true\n",[1164,1836,1837,1839],{"class":1166,"line":51},[1164,1838,1816],{"class":1815},[1164,1840,1841],{"class":1819}," traefik.http.services.\u003Csvc>.loadbalancer.sticky.cookie.httponly=true\n",[1012,1843,1844,1854],{},[1015,1845,1846],{},[1018,1847,1848,1851],{},[1021,1849,1850],{},"Problem",[1021,1852,1853],{},"Sticky cookie fixes it?",[1031,1855,1856,1864,1876,1886,1894],{},[1018,1857,1858,1861],{},[1036,1859,1860],{},"Per-task in-memory state (local cache, upload in progress, multi-step flow kept in memory)",[1036,1862,1863],{},"✅",[1018,1865,1866,1869],{},[1036,1867,1868],{},"WebSocket \u002F SSE reconnecting to the same task",[1036,1870,1871,1872,1875],{},"✅ (though prefer a shared hub — ",[1042,1873,1874],{},"go-modules\u002Fws"," fans out across replicas)",[1018,1877,1878,1883],{},[1036,1879,1880,1881],{},"Seeing your own Mongo write when the task reads ",[1042,1882,1097],{},[1036,1884,1885],{},"❌ — the write went to the primary; the sticky task's nearby member may still lag. Use §2's read-your-writes.",[1018,1887,1888,1891],{},[1036,1889,1890],{},"Cross-region latency",[1036,1892,1893],{},"❌ — it pins a task, not a region. Region affinity belongs at the edge (§3 model B).",[1018,1895,1896,1899],{},[1036,1897,1898],{},"Deploys",[1036,1900,1901],{},"⚠️ a new task gets a new identity, so every sticky user moves at once on each rolling update",[991,1903,1904],{},"Rule: make app tasks stateless (shared state in Redis\u002FMongo), choose\nconsistency per read (§2), choose region at the edge (§3). Add a sticky cookie\nonly for genuinely per-task state.",[1007,1906,1908],{"id":1907},"_5-placement","5. Placement",[1187,1910,1911,1920,1933,1936,1942],{},[1190,1912,1913,1569,1916,1919],{},[997,1914,1915],{},"Constraints are hard; preferences only spread.",[1042,1917,1918],{},"placement.preferences: [spread: node.labels.X]"," balances tasks evenly across label values — there is\nno soft \"prefer this node, fall back to that one\". Two replicas over two\neligible nodes always land 1 + 1.",[1190,1921,1922,1923,1071,1926,1616,1929,1932],{},"Label nodes by role and location (",[1042,1924,1925],{},"location=\u003Cregion>",[1042,1927,1928],{},"api=true",[1042,1930,1931],{},"clickhouse=true"," …) and constrain on labels, not hostnames.",[1190,1934,1935],{},"A service pinned to one region has no automatic fallback. Either run it in\ntwo regions (and accept their latency), or keep the failover step written\ndown (relabel the node, redeploy).",[1190,1937,1938,1941],{},[1042,1939,1940],{},"max_replicas_per_node"," keeps replicas apart within a region.",[1190,1943,1944,1945,1948,1949,1952],{},"Rolling updates: ",[1042,1946,1947],{},"update_config.order: start-first"," plus\n",[1042,1950,1951],{},"SHUTDOWN_DRAIN_SECONDS"," (go-framework ≥ v0.15.4) so the old task keeps\nserving until the proxy has noticed the new one.",[1007,1954,1956],{"id":1955},"_6-events-across-nodes","6. Events across nodes",[1187,1958,1959,1962],{},[1190,1960,1961],{},"Work queues: one consumer group per handler; Kafka spreads partitions across\nreplicas. Publishing from a request is a remote call — do it asynchronously\nor after the response where possible.",[1190,1963,1964,1965,1968,1969,1972],{},"Broadcasts (cache invalidation, template reload): every replica must get\nevery message. go-events ",[997,1966,1967],{},"v0.4.5"," (v0.3.1 on the 0.3 line; avoid v0.4.1–v0.4.4, whose topic checks stall startup for seconds per topic) consumes the known broadcast group prefixes\n(",[1042,1970,1971],{},"KAFKA_BROADCAST_GROUP_PREFIXES",") without a consumer group, so restarts leave\nnothing behind on the broker.",[1007,1974,1976],{"id":1975},"_7-measure-per-replica","7. Measure, per replica",[991,1978,1979],{},"Average latency hides a slow replica. Break request histograms down by instance\nand map instances to nodes:",[1155,1981,1985],{"className":1982,"code":1983,"language":1984,"meta":1160,"style":1160},"language-promql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","histogram_quantile(0.5,  sum by (le) (rate(http_request_duration_seconds_bucket{job=\"\u003Capp>\",service=\"\u003Csvc>\",instance=\"\u003Ctask-ip>:\u003Cport>\"}[1h])))\nhistogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket{job=\"\u003Capp>\",service=\"\u003Csvc>\",instance=\"\u003Ctask-ip>:\u003Cport>\"}[1h])))\n","promql",[1042,1986,1987,1992],{"__ignoreMap":1160},[1164,1988,1989],{"class":1166,"line":10},[1164,1990,1991],{},"histogram_quantile(0.5,  sum by (le) (rate(http_request_duration_seconds_bucket{job=\"\u003Capp>\",service=\"\u003Csvc>\",instance=\"\u003Ctask-ip>:\u003Cport>\"}[1h])))\n",[1164,1993,1994],{"class":1166,"line":16},[1164,1995,1996],{},"histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket{job=\"\u003Capp>\",service=\"\u003Csvc>\",instance=\"\u003Ctask-ip>:\u003Cport>\"}[1h])))\n",[1155,1998,2002],{"className":1999,"code":2000,"language":2001,"meta":1160,"style":1160},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","docker service ps \u003Cstack>_\u003Csvc> --filter desired-state=running --format '{{.Node}} {{.ID}}'\ndocker inspect \u003Ctask-id> --format '{{range .NetworksAttachments}}{{.Network.Spec.Name}}={{.Addresses}} {{end}}'\nping -c 5 \u003Cother-node>    # node-to-node RTT; every remote data call costs at least this\n","bash",[1042,2003,2004,2061,2087],{"__ignoreMap":1160},[1164,2005,2006,2010,2013,2016,2019,2022,2026,2029,2032,2035,2038,2041,2043,2046,2049,2052,2055,2058],{"class":1166,"line":10},[1164,2007,2009],{"class":2008},"sBMFI","docker",[1164,2011,2012],{"class":1819}," service",[1164,2014,2015],{"class":1819}," ps",[1164,2017,2018],{"class":1815}," \u003C",[1164,2020,2021],{"class":1819},"stac",[1164,2023,2025],{"class":2024},"sTEyZ","k",[1164,2027,2028],{"class":1815},">",[1164,2030,2031],{"class":1819},"_",[1164,2033,2034],{"class":1815},"\u003C",[1164,2036,2037],{"class":1819},"sv",[1164,2039,2040],{"class":2024},"c",[1164,2042,2028],{"class":1815},[1164,2044,2045],{"class":1819}," --filter",[1164,2047,2048],{"class":1819}," desired-state=running",[1164,2050,2051],{"class":1819}," --format",[1164,2053,2054],{"class":1815}," '",[1164,2056,2057],{"class":1819},"{{.Node}} {{.ID}}",[1164,2059,2060],{"class":1815},"'\n",[1164,2062,2063,2065,2068,2070,2073,2076,2078,2080,2082,2085],{"class":1166,"line":16},[1164,2064,2009],{"class":2008},[1164,2066,2067],{"class":1819}," inspect",[1164,2069,2018],{"class":1815},[1164,2071,2072],{"class":1819},"task-i",[1164,2074,2075],{"class":2024},"d",[1164,2077,2028],{"class":1815},[1164,2079,2051],{"class":1819},[1164,2081,2054],{"class":1815},[1164,2083,2084],{"class":1819},"{{range .NetworksAttachments}}{{.Network.Spec.Name}}={{.Addresses}} {{end}}",[1164,2086,2060],{"class":1815},[1164,2088,2089,2092,2095,2099,2101,2104,2107,2109],{"class":1166,"line":22},[1164,2090,2091],{"class":2008},"ping",[1164,2093,2094],{"class":1819}," -c",[1164,2096,2098],{"class":2097},"sbssI"," 5",[1164,2100,2018],{"class":1815},[1164,2102,2103],{"class":1819},"other-nod",[1164,2105,2106],{"class":2024},"e",[1164,2108,2028],{"class":1815},[1164,2110,2112],{"class":2111},"sHwdD","    # node-to-node RTT; every remote data call costs at least this\n",[991,2114,2115,2116,2119],{},"Count the sequential data calls per request (Mongo, shared Redis, synchronous\npublishes). ",[1002,2117,2118],{},"calls × RTT"," is the floor a remote replica adds.",[1007,2121,2123],{"id":2122},"testing-read-preference","Testing read preference",[1187,2125,2126,2207,2220],{},[1190,2127,2128,2131,2132,2135,2136,2139,2140,2203,2206],{},[997,2129,2130],{},"Test on a replica set."," A standalone server or ",[1042,2133,2134],{},"directConnection=true","\nmakes the driver drop ",[1042,2137,2138],{},"$readPreference",", so a test there passes for the\nwrong reason. A single-member set is enough:",[1155,2141,2143],{"className":1999,"code":2142,"language":2001,"meta":1160,"style":1160},"docker run -d -p 27199:27199 mongo:7 --replSet rs0 --port 27199\nmongosh --port 27199 --eval \"rs.initiate({_id:'rs0',members:[{_id:0,host:'localhost:27199'}]})\"\n# mongodb:\u002F\u002Flocalhost:27199\u002F?replicaSet=rs0\n",[1042,2144,2145,2176,2198],{"__ignoreMap":1160},[1164,2146,2147,2149,2152,2155,2158,2161,2164,2167,2170,2173],{"class":1166,"line":10},[1164,2148,2009],{"class":2008},[1164,2150,2151],{"class":1819}," run",[1164,2153,2154],{"class":1819}," -d",[1164,2156,2157],{"class":1819}," -p",[1164,2159,2160],{"class":1819}," 27199:27199",[1164,2162,2163],{"class":1819}," mongo:7",[1164,2165,2166],{"class":1819}," --replSet",[1164,2168,2169],{"class":1819}," rs0",[1164,2171,2172],{"class":1819}," --port",[1164,2174,2175],{"class":2097}," 27199\n",[1164,2177,2178,2181,2183,2186,2189,2192,2195],{"class":1166,"line":16},[1164,2179,2180],{"class":2008},"mongosh",[1164,2182,2172],{"class":1819},[1164,2184,2185],{"class":2097}," 27199",[1164,2187,2188],{"class":1819}," --eval",[1164,2190,2191],{"class":1815}," \"",[1164,2193,2194],{"class":1819},"rs.initiate({_id:'rs0',members:[{_id:0,host:'localhost:27199'}]})",[1164,2196,2197],{"class":1815},"\"\n",[1164,2199,2200],{"class":1166,"line":22},[1164,2201,2202],{"class":2111},"# mongodb:\u002F\u002Flocalhost:27199\u002F?replicaSet=rs0\n",[2204,2205],"br",{},"The port must match inside and outside the container: a member must find\nitself in its own config. In CI, pick a free port first.",[1190,2208,2209,2212,2213,2216,2217,2219],{},[997,2210,2211],{},"Assert on the wire:"," use a ",[1042,2214,2215],{},"CommandMonitor",", and check ",[1042,2218,2138],{},"\non the command each classified read sends.",[1190,2221,2222,2225],{},[997,2223,2224],{},"Watch for TTL time bombs."," Real-data fixtures with fixed dates fall out\nof a table's TTL (e.g. 7 days) and ClickHouse drops them on insert; the\ntests start failing on a date, on every commit. Create test tables without\nthe TTL.",[1007,2227,2229],{"id":2228},"checklist","Checklist",[1187,2231,2234,2243,2255,2270,2279,2285,2291,2297,2303],{"className":2232},[2233],"contains-task-list",[1190,2235,2238,2242],{"className":2236},[2237],"task-list-item",[2239,2240],"input",{"disabled":1174,"type":2241},"checkbox"," Node labels for role and location; services constrained by labels.",[1190,2244,2246,2248,2249,2252,2253,1454],{"className":2245},[2237],[2239,2247],{"disabled":1174,"type":2241}," Per-node services named ",[1042,2250,2251],{},"\u003Csvc>-\u003Chostname>","; app env uses ",[1042,2254,1131],{},[1190,2256,2258,1569,2260,2263,2264,2267,2268,1454],{"className":2257},[2237],[2239,2259],{"disabled":1174,"type":2241},[1042,2261,2262],{},"MONGO_URI"," lists every member with ",[1042,2265,2266],{},"replicaSet=","; default read preference stays ",[1042,2269,1311],{},[1190,2271,2273,2275,2276,2278],{"className":2272},[2237],[2239,2274],{"disabled":1174,"type":2241}," go-framework ≥ v0.16.6 if any read uses ",[1042,2277,1097],{},"; raw collections cloned with the preference.",[1190,2280,2282,2284],{"className":2281},[2237],[2239,2283],{"disabled":1174,"type":2241}," Every hot-path read classified (Nearest \u002F primary \u002F read-your-writes pattern), recorded with the code.",[1190,2286,2288,2290],{"className":2287},[2237],[2239,2289],{"disabled":1174,"type":2241}," Routing model chosen: A (app next to primary), B (regional services + geo-steering edge), or client-chosen region (regional hosts + client RTT choice).",[1190,2292,2294,2296],{"className":2293},[2237],[2239,2295],{"disabled":1174,"type":2241}," Sticky cookies only where a task holds per-request state.",[1190,2298,2300,2302],{"className":2299},[2237],[2239,2301],{"disabled":1174,"type":2241}," go-events ≥ v0.4.5 (or v0.3.1): broadcast consumers, and no per-topic startup stall.",[1190,2304,2306,2308],{"className":2305},[2237],[2239,2307],{"disabled":1174,"type":2241}," Per-replica p50\u002Fp95 dashboards; RTT between nodes known.",[2310,2311,2312],"style",{},"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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":1160,"searchDepth":16,"depth":16,"links":2314},[2315,2316,2319,2322,2323,2324,2325,2326,2327],{"id":1009,"depth":16,"text":1010},{"id":1142,"depth":16,"text":1143,"children":2317},[2318],{"id":1322,"depth":22,"text":1323},{"id":1482,"depth":16,"text":1483,"children":2320},[2321],{"id":1578,"depth":22,"text":1579},{"id":1795,"depth":16,"text":1796},{"id":1907,"depth":16,"text":1908},{"id":1955,"depth":16,"text":1956},{"id":1975,"depth":16,"text":1976},{"id":2122,"depth":16,"text":2123},{"id":2228,"depth":16,"text":2229},"Run a Redelay project on several Swarm nodes or regions — which data services are node-local and which are not, how requests are routed, when sticky sessions help, and how to read from the nearest replica without losing read-your-writes.","md",null,{},{"icon":41},{"title":87,"description":2328},"lnKDdf2u8Pvvbp0t7m7HIQI70M32jAajeg5PrvyIWZQ",1791986166649]