[{"data":1,"prerenderedAt":1542},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fmigration":314,"\u002Fdocs\u002Fmigration-surround":1537},[4,38,86,149,176,219,257,273],{"title":5,"path":6,"stem":7,"children":8,"icon":37},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,17,22,27,32],{"title":10,"path":6,"stem":7,"icon":11},"Getting started","i-lucide-flag",{"title":13,"path":14,"stem":15,"icon":16},"Modules overview","\u002Fdocs\u002Fgetting-started\u002Fmodules-overview","1.docs\u002F1.getting-started\u002F2.modules-overview","i-lucide-boxes",{"title":18,"path":19,"stem":20,"icon":21},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F3.installation","i-lucide-download",{"title":23,"path":24,"stem":25,"icon":26},"License configuration","\u002Fdocs\u002Fgetting-started\u002Flicense-configuration","1.docs\u002F1.getting-started\u002F4.license-configuration","i-lucide-key-round",{"title":28,"path":29,"stem":30,"icon":31},"Your first app","\u002Fdocs\u002Fgetting-started\u002Ffirst-app","1.docs\u002F1.getting-started\u002F5.first-app","i-lucide-square-play",{"title":33,"path":34,"stem":35,"icon":36},"Project setup","\u002Fdocs\u002Fgetting-started\u002Fproject-setup","1.docs\u002F1.getting-started\u002F6.project-setup","i-lucide-package",false,{"title":39,"path":40,"stem":41,"children":42,"icon":45},"Core Concepts","\u002Fdocs\u002Fconcepts","1.docs\u002F2.concepts\u002F1.index",[43,46,51,56,61,66,71,76,81],{"title":44,"path":40,"stem":41,"icon":45},"Core concepts","i-lucide-book-open",{"title":47,"path":48,"stem":49,"icon":50},"Architecture","\u002Fdocs\u002Fconcepts\u002Farchitecture","1.docs\u002F2.concepts\u002F2.architecture","i-lucide-layers",{"title":52,"path":53,"stem":54,"icon":55},"Exactly-once semantics","\u002Fdocs\u002Fconcepts\u002Fexactly-once","1.docs\u002F2.concepts\u002F3.exactly-once","i-lucide-shield-check",{"title":57,"path":58,"stem":59,"icon":60},"Lanes and parallelism","\u002Fdocs\u002Fconcepts\u002Flanes-and-parallelism","1.docs\u002F2.concepts\u002F4.lanes-and-parallelism","i-lucide-split",{"title":62,"path":63,"stem":64,"icon":65},"State and thread-safety","\u002Fdocs\u002Fconcepts\u002Fstate-and-thread-safety","1.docs\u002F2.concepts\u002F5.state-and-thread-safety","i-lucide-lock",{"title":67,"path":68,"stem":69,"icon":70},"Event time and watermarks","\u002Fdocs\u002Fconcepts\u002Fevent-time-and-watermarks","1.docs\u002F2.concepts\u002F6.event-time-and-watermarks","i-lucide-clock",{"title":72,"path":73,"stem":74,"icon":75},"The configuration model","\u002Fdocs\u002Fconcepts\u002Fconfiguration-model","1.docs\u002F2.concepts\u002F7.configuration-model","i-lucide-sliders-horizontal",{"title":77,"path":78,"stem":79,"icon":80},"The error-handling model","\u002Fdocs\u002Fconcepts\u002Ferror-handling-model","1.docs\u002F2.concepts\u002F8.error-handling-model","i-lucide-triangle-alert",{"title":82,"path":83,"stem":84,"icon":85},"How StoatFlow differs from Kafka Streams","\u002Fdocs\u002Fconcepts\u002Fhow-stoatflow-differs-from-ks","1.docs\u002F2.concepts\u002F9.how-stoatflow-differs-from-ks","i-lucide-arrow-left-right",{"title":87,"path":88,"stem":89,"children":90,"icon":93},"Building Topologies","\u002Fdocs\u002Fbuilding","1.docs\u002F3.building\u002F1.index",[91,94,99,104,109,114,119,124,129,134,139,144],{"title":92,"path":88,"stem":89,"icon":93},"Building topologies","i-lucide-blocks",{"title":95,"path":96,"stem":97,"icon":98},"Error handling and DLQ","\u002Fdocs\u002Fbuilding\u002Ferror-handling-dlq","1.docs\u002F3.building\u002F10.error-handling-dlq","i-lucide-circle-x",{"title":100,"path":101,"stem":102,"icon":103},"State stores","\u002Fdocs\u002Fbuilding\u002Fstate-stores","1.docs\u002F3.building\u002F11.state-stores","i-lucide-database",{"title":105,"path":106,"stem":107,"icon":108},"Testing topologies","\u002Fdocs\u002Fbuilding\u002Ftesting","1.docs\u002F3.building\u002F12.testing","i-lucide-flask-conical",{"title":110,"path":111,"stem":112,"icon":113},"Sources and sinks","\u002Fdocs\u002Fbuilding\u002Fstreams-builder","1.docs\u002F3.building\u002F2.streams-builder","i-lucide-import",{"title":115,"path":116,"stem":117,"icon":118},"KStream and KTable operations","\u002Fdocs\u002Fbuilding\u002Fkstream-ktable","1.docs\u002F3.building\u002F3.kstream-ktable","i-lucide-waypoints",{"title":120,"path":121,"stem":122,"icon":123},"Aggregations","\u002Fdocs\u002Fbuilding\u002Faggregations","1.docs\u002F3.building\u002F4.aggregations","i-lucide-sigma",{"title":125,"path":126,"stem":127,"icon":128},"Windowing","\u002Fdocs\u002Fbuilding\u002Fwindowing","1.docs\u002F3.building\u002F5.windowing","i-lucide-calendar-clock",{"title":130,"path":131,"stem":132,"icon":133},"Joins","\u002Fdocs\u002Fbuilding\u002Fjoins","1.docs\u002F3.building\u002F6.joins","i-lucide-git-merge",{"title":135,"path":136,"stem":137,"icon":138},"The Processor API","\u002Fdocs\u002Fbuilding\u002Fprocessor-api","1.docs\u002F3.building\u002F7.processor-api","i-lucide-cpu",{"title":140,"path":141,"stem":142,"icon":143},"Scheduled sources","\u002Fdocs\u002Fbuilding\u002Fscheduled-sources","1.docs\u002F3.building\u002F8.scheduled-sources","i-lucide-timer",{"title":145,"path":146,"stem":147,"icon":148},"Serdes and Avro","\u002Fdocs\u002Fbuilding\u002Fserdes","1.docs\u002F3.building\u002F9.serdes","i-lucide-binary",{"title":150,"path":151,"stem":152,"children":153,"icon":75},"Configuration","\u002Fdocs\u002Fconfiguration","1.docs\u002F4.configuration\u002F1.index",[154,156,161,166,171],{"title":155,"path":151,"stem":152,"icon":75},"How configuration works",{"title":157,"path":158,"stem":159,"icon":160},"Engine configuration (:core)","\u002Fdocs\u002Fconfiguration\u002Fcore-config","1.docs\u002F4.configuration\u002F2.core-config","i-lucide-settings-2",{"title":162,"path":163,"stem":164,"icon":165},"Runtime configuration (:runtime)","\u002Fdocs\u002Fconfiguration\u002Fruntime-config","1.docs\u002F4.configuration\u002F3.runtime-config","i-lucide-server-cog",{"title":167,"path":168,"stem":169,"icon":170},"Defaults, adaptivity, and presets","\u002Fdocs\u002Fconfiguration\u002Fdefaults-and-presets","1.docs\u002F4.configuration\u002F4.defaults-and-presets","i-lucide-gauge",{"title":172,"path":173,"stem":174,"icon":175},"Kafka client configuration","\u002Fdocs\u002Fconfiguration\u002Fkafka-client-config","1.docs\u002F4.configuration\u002F5.kafka-client-config","i-lucide-plug",{"title":177,"path":178,"stem":179,"children":180,"icon":183},"Running in Production","\u002Fdocs\u002Fruntime","1.docs\u002F5.runtime\u002F1.index",[181,184,189,194,199,204,209,214],{"title":182,"path":178,"stem":179,"icon":183},"The runtime","i-lucide-server",{"title":185,"path":186,"stem":187,"icon":188},"The REST API","\u002Fdocs\u002Fruntime\u002Frest-api","1.docs\u002F5.runtime\u002F2.rest-api","i-lucide-globe",{"title":190,"path":191,"stem":192,"icon":193},"Health checks","\u002Fdocs\u002Fruntime\u002Fhealth-checks","1.docs\u002F5.runtime\u002F3.health-checks","i-lucide-heart-pulse",{"title":195,"path":196,"stem":197,"icon":198},"Metrics","\u002Fdocs\u002Fruntime\u002Fmetrics","1.docs\u002F5.runtime\u002F4.metrics","i-lucide-activity",{"title":200,"path":201,"stem":202,"icon":203},"Pause and resume","\u002Fdocs\u002Fruntime\u002Fpause-unpause","1.docs\u002F5.runtime\u002F5.pause-unpause","i-lucide-pause",{"title":205,"path":206,"stem":207,"icon":208},"Plugins and lifecycle hooks","\u002Fdocs\u002Fruntime\u002Fplugins","1.docs\u002F5.runtime\u002F6.plugins","i-lucide-puzzle",{"title":210,"path":211,"stem":212,"icon":213},"Docker images","\u002Fdocs\u002Fruntime\u002Fdocker","1.docs\u002F5.runtime\u002F7.docker","i-lucide-container",{"title":215,"path":216,"stem":217,"icon":218},"GraalVM native image","\u002Fdocs\u002Fruntime\u002Fnative-image","1.docs\u002F5.runtime\u002F8.native-image","i-lucide-zap",{"title":220,"path":221,"stem":222,"children":223,"icon":226},"Deploying & Operating","\u002Fdocs\u002Foperating","1.docs\u002F6.operating\u002F1.index",[224,227,232,237,242,247,252],{"title":225,"path":221,"stem":222,"icon":226},"Deploying and operating","i-lucide-life-buoy",{"title":228,"path":229,"stem":230,"icon":231},"Running on Kubernetes","\u002Fdocs\u002Foperating\u002Fkubernetes","1.docs\u002F6.operating\u002F2.kubernetes","i-lucide-ship",{"title":233,"path":234,"stem":235,"icon":236},"High availability","\u002Fdocs\u002Foperating\u002Fhigh-availability","1.docs\u002F6.operating\u002F3.high-availability","i-lucide-copy",{"title":238,"path":239,"stem":240,"icon":241},"Liveness and readiness probes","\u002Fdocs\u002Foperating\u002Fprobes","1.docs\u002F6.operating\u002F4.probes","i-lucide-stethoscope",{"title":243,"path":244,"stem":245,"icon":246},"Observability","\u002Fdocs\u002Foperating\u002Fobservability","1.docs\u002F6.operating\u002F5.observability","i-lucide-telescope",{"title":248,"path":249,"stem":250,"icon":251},"Tuning under load","\u002Fdocs\u002Foperating\u002Ftuning","1.docs\u002F6.operating\u002F6.tuning","i-lucide-sliders",{"title":253,"path":254,"stem":255,"icon":256},"Production checklist","\u002Fdocs\u002Foperating\u002Fproduction-checklist","1.docs\u002F6.operating\u002F7.production-checklist","i-lucide-clipboard-check",{"title":258,"path":259,"stem":260,"children":261,"icon":85},"Migrating from Kafka Streams","\u002Fdocs\u002Fmigration","1.docs\u002F7.migration\u002F1.index",[262,263,268],{"title":258,"path":259,"stem":260,"icon":85},{"title":264,"path":265,"stem":266,"icon":267},"Migration without carrying state","\u002Fdocs\u002Fmigration\u002Fwithout-data-migration","1.docs\u002F7.migration\u002F2.without-data-migration","i-lucide-sparkles",{"title":269,"path":270,"stem":271,"icon":272},"Migration carrying state","\u002Fdocs\u002Fmigration\u002Fwith-data-migration","1.docs\u002F7.migration\u002F3.with-data-migration","i-lucide-database-backup",{"title":274,"path":275,"stem":276,"children":277,"icon":279},"Reference","\u002Fdocs\u002Freference","1.docs\u002F8.reference\u002F1.index",[278,280,285,290,295,300,305,309],{"title":274,"path":275,"stem":276,"icon":279},"i-lucide-list",{"title":281,"path":282,"stem":283,"icon":284},"Configuration reference","\u002Fdocs\u002Freference\u002Fconfiguration-reference","1.docs\u002F8.reference\u002F2.configuration-reference","i-lucide-table",{"title":286,"path":287,"stem":288,"icon":289},"REST API reference","\u002Fdocs\u002Freference\u002Frest-api-reference","1.docs\u002F8.reference\u002F3.rest-api-reference","i-lucide-network",{"title":291,"path":292,"stem":293,"icon":294},"Gradle plugin reference","\u002Fdocs\u002Freference\u002Fgradle-plugin-reference","1.docs\u002F8.reference\u002F4.gradle-plugin-reference","i-lucide-box",{"title":296,"path":297,"stem":298,"icon":299},"Maven reference","\u002Fdocs\u002Freference\u002Fmaven-reference","1.docs\u002F8.reference\u002F5.maven-reference","i-simple-icons-apachemaven",{"title":301,"path":302,"stem":303,"icon":304},"Kafka Streams compatibility matrix","\u002Fdocs\u002Freference\u002Fks-compatibility-matrix","1.docs\u002F8.reference\u002F6.ks-compatibility-matrix","i-lucide-table-2",{"title":306,"path":307,"stem":308,"icon":170},"Metrics reference","\u002Fdocs\u002Freference\u002Fmetrics-reference","1.docs\u002F8.reference\u002F7.metrics-reference",{"title":310,"path":311,"stem":312,"icon":313},"Glossary","\u002Fdocs\u002Freference\u002Fglossary","1.docs\u002F8.reference\u002F8.glossary","i-lucide-book-a",{"id":315,"title":258,"body":316,"description":1530,"extension":1531,"meta":1532,"navigation":1533,"pageType":1534,"path":259,"seo":1535,"stem":260,"__hash__":1536},"docs\u002F1.docs\u002F7.migration\u002F1.index.md",{"type":317,"value":318,"toc":1517},"minimark",[319,323,379,384,421,424,968,971,1034,1038,1041,1115,1133,1138,1141,1195,1213,1217,1248,1252,1258,1261,1264,1316,1330,1334,1344,1374,1377,1381,1386,1406,1409,1420,1424,1427,1474,1478,1513],[320,321,322],"p",{},"Moving a Kafka Streams application to StoatFlow is mostly a port, not a rewrite: the part you spend the most time on — the topology — carries over largely unchanged, and what changes is the runtime model around it. This page frames the migration conceptually: what stays the same, what differs operationally, and how to choose between starting clean and carrying state across. The two sub-pages then walk the concrete steps for each path.",[324,325,326],"tldr-panel",{},[327,328,329,352,358,364],"ul",{},[330,331,332,336,337,341,342,341,345,341,348,351],"li",{},[333,334,335],"strong",{},"The topology code carries over."," Same DSL, same Processor API, same ",[338,339,340],"code",{},"Consumed"," \u002F ",[338,343,344],{},"Produced",[338,346,347],{},"Materialized",[338,349,350],{},"Grouped"," config objects. The imports and the entry point change; the operators don't.",[330,353,354,357],{},[333,355,356],{},"The runtime model changes."," Single instance instead of a rebalancing cluster, in-memory re-keying instead of repartition topics, one commit barrier instead of per-task transactions, global state instead of partition-scoped state.",[330,359,360,363],{},[333,361,362],{},"The decision that drives everything: state."," A topology with no meaningful state, or one you can rebuild by reprocessing input, is a clean cutover. A topology whose correctness depends on accumulated state needs a state-carrying plan.",[330,365,366,369,370,374,375,378],{},[333,367,368],{},"Two paths:"," ",[371,372,373],"a",{"href":265},"Without data migration"," (green-field cutover, reprocess from input) and ",[371,376,377],{"href":270},"With data migration"," (carry existing state across).",[380,381,383],"h2",{"id":382},"what-carries-over-unchanged","What carries over unchanged",[320,385,386,387,390,391,341,394,341,397,400,401,390,404,390,407,390,410,390,413,416,417,420],{},"StoatFlow implements the Kafka Streams DSL. The topology you already wrote — ",[338,388,389],{},"StreamsBuilder",", ",[338,392,393],{},"KStream",[338,395,396],{},"KTable",[338,398,399],{},"KGroupedStream",", the windowed and session variants, the joins, the Processor API, and the KS-compatible functional interfaces (",[338,402,403],{},"ValueMapper",[338,405,406],{},"KeyValueMapper",[338,408,409],{},"ValueJoiner",[338,411,412],{},"Reducer",[338,414,415],{},"Aggregator",", …) — is the same code on both engines. The method-by-method status is in the ",[371,418,419],{"href":302},"KS compatibility matrix",".",[320,422,423],{},"The same map-filter chain is written the same way against either engine:",[425,426,428,682],"code-tabs",{"group":427},"lang",[429,430,436],"pre",{"className":431,"code":432,"filename":433,"language":434,"meta":435,"style":435},"language-kotlin shiki shiki-themes vitesse-light","val intermediate =\n    stream1\n        .selectKey { _, _ -> \"lala\" }\n        .map { key, value -> KeyValue(value.substring(0, 3), \"$key:$value\") }\n        .filter { _, value -> value.length > 5 }\n        .mapValues { value -> value.uppercase() }\n\nintermediate.to(\n    \"output-topic\",\n    Produced.`as`\u003CString, String>(\"sink1\")\n        .withKeySerde(Serdes.String())\n        .withValueSerde(Serdes.String()),\n)\n","Kotlin","kotlin","",[338,437,438,455,461,484,535,564,589,596,608,617,646,662,677],{"__ignoreMap":435},[439,440,443,447,451],"span",{"class":441,"line":442},"line",1,[439,444,446],{"class":445},"sbBg2","val",[439,448,450],{"class":449},"suHK_"," intermediate ",[439,452,454],{"class":453},"sYZai","=\n",[439,456,458],{"class":441,"line":457},2,[439,459,460],{"class":449},"    stream1\n",[439,462,464,467,471,474,477,481],{"class":441,"line":463},3,[439,465,466],{"class":449},"        .",[439,468,470],{"class":469},"sySUi","selectKey",[439,472,473],{"class":449}," { _, _ ",[439,475,476],{"class":453},"->",[439,478,480],{"class":479},"spphp"," \"lala\"",[439,482,483],{"class":449}," }\n",[439,485,487,489,492,495,499,502,505,508,510,512,515,517,521,523,526,529,532],{"class":441,"line":486},4,[439,488,466],{"class":449},[439,490,491],{"class":469},"map",[439,493,494],{"class":449}," { key, ",[439,496,498],{"class":497},"si04Y","value",[439,500,501],{"class":453}," ->",[439,503,504],{"class":469}," KeyValue",[439,506,507],{"class":449},"(",[439,509,498],{"class":497},[439,511,420],{"class":449},[439,513,514],{"class":469},"substring",[439,516,507],{"class":449},[439,518,520],{"class":519},"s-TwI","0",[439,522,390],{"class":449},[439,524,525],{"class":519},"3",[439,527,528],{"class":449},"), ",[439,530,531],{"class":479},"\"$key:$value\"",[439,533,534],{"class":449},") }\n",[439,536,538,540,543,546,548,550,553,556,559,562],{"class":441,"line":537},5,[439,539,466],{"class":449},[439,541,542],{"class":469},"filter",[439,544,545],{"class":449}," { _, ",[439,547,498],{"class":497},[439,549,501],{"class":453},[439,551,552],{"class":497}," value",[439,554,555],{"class":449},".length ",[439,557,558],{"class":497},">",[439,560,561],{"class":519}," 5",[439,563,483],{"class":449},[439,565,567,569,572,575,577,579,581,583,586],{"class":441,"line":566},6,[439,568,466],{"class":449},[439,570,571],{"class":469},"mapValues",[439,573,574],{"class":449}," { ",[439,576,498],{"class":497},[439,578,501],{"class":453},[439,580,552],{"class":497},[439,582,420],{"class":449},[439,584,585],{"class":469},"uppercase",[439,587,588],{"class":449},"() }\n",[439,590,592],{"class":441,"line":591},7,[439,593,595],{"emptyLinePlaceholder":594},true,"\n",[439,597,599,602,605],{"class":441,"line":598},8,[439,600,601],{"class":449},"intermediate.",[439,603,604],{"class":469},"to",[439,606,607],{"class":449},"(\n",[439,609,611,614],{"class":441,"line":610},9,[439,612,613],{"class":479},"    \"output-topic\"",[439,615,616],{"class":449},",\n",[439,618,620,623,626,629,633,635,637,640,643],{"class":441,"line":619},10,[439,621,622],{"class":449},"    Produced.",[439,624,625],{"class":469},"`as`",[439,627,628],{"class":449},"\u003C",[439,630,632],{"class":631},"sUxyF","String",[439,634,390],{"class":449},[439,636,632],{"class":631},[439,638,639],{"class":449},">(",[439,641,642],{"class":479},"\"sink1\"",[439,644,645],{"class":449},")\n",[439,647,649,651,654,657,659],{"class":441,"line":648},11,[439,650,466],{"class":449},[439,652,653],{"class":469},"withKeySerde",[439,655,656],{"class":449},"(Serdes.",[439,658,632],{"class":469},[439,660,661],{"class":449},"())\n",[439,663,665,667,670,672,674],{"class":441,"line":664},12,[439,666,466],{"class":449},[439,668,669],{"class":469},"withValueSerde",[439,671,656],{"class":449},[439,673,632],{"class":469},[439,675,676],{"class":449},"()),\n",[439,678,680],{"class":441,"line":679},13,[439,681,645],{"class":449},[429,683,688],{"className":684,"code":685,"filename":686,"language":687,"meta":435,"style":435},"language-java shiki shiki-themes vitesse-light","KStream\u003CString, String> intermediate =\n    stream1\n        .selectKey((k, v) -> \"lala\")\n        .map((key, value) -> KeyValue.pair(value.substring(0, 3), key + \":\" + value))\n        .filter((k, value) -> value.length() > 5)\n        .mapValues(value -> value.toUpperCase());\n\nintermediate.to(\n    \"output-topic\",\n    Produced.\u003CString, String>as(\"sink1\")\n        .withKeySerde(Serdes.String())\n        .withValueSerde(Serdes.String()));\n","Java","java",[338,689,690,713,717,751,818,853,876,880,891,903,934,951],{"__ignoreMap":435},[439,691,692,694,696,698,701,704,706,710],{"class":441,"line":442},[439,693,393],{"class":449},[439,695,628],{"class":453},[439,697,632],{"class":497},[439,699,700],{"class":453},",",[439,702,703],{"class":497}," String",[439,705,558],{"class":453},[439,707,709],{"class":708},"svycV"," intermediate",[439,711,712],{"class":453}," =\n",[439,714,715],{"class":441,"line":457},[439,716,460],{"class":449},[439,718,719,721,723,726,729,731,734,737,739,743,746,749],{"class":441,"line":463},[439,720,466],{"class":453},[439,722,470],{"class":469},[439,724,725],{"class":453},"((",[439,727,728],{"class":449},"k",[439,730,700],{"class":453},[439,732,733],{"class":449}," v",[439,735,736],{"class":453},")",[439,738,501],{"class":453},[439,740,742],{"class":741},"sSP4y"," \"",[439,744,745],{"class":479},"lala",[439,747,748],{"class":741},"\"",[439,750,645],{"class":453},[439,752,753,755,757,759,762,764,766,768,770,772,774,777,779,781,783,785,787,789,791,794,797,800,803,805,808,810,813,815],{"class":441,"line":486},[439,754,466],{"class":453},[439,756,491],{"class":469},[439,758,725],{"class":453},[439,760,761],{"class":449},"key",[439,763,700],{"class":453},[439,765,552],{"class":449},[439,767,736],{"class":453},[439,769,501],{"class":453},[439,771,504],{"class":708},[439,773,420],{"class":453},[439,775,776],{"class":469},"pair",[439,778,507],{"class":453},[439,780,498],{"class":708},[439,782,420],{"class":453},[439,784,514],{"class":469},[439,786,507],{"class":453},[439,788,520],{"class":519},[439,790,700],{"class":453},[439,792,793],{"class":519}," 3",[439,795,796],{"class":453},"),",[439,798,799],{"class":449}," key ",[439,801,802],{"class":497},"+",[439,804,742],{"class":741},[439,806,807],{"class":479},":",[439,809,748],{"class":741},[439,811,812],{"class":497}," +",[439,814,552],{"class":449},[439,816,817],{"class":453},"))\n",[439,819,820,822,824,826,828,830,832,834,836,838,840,843,846,849,851],{"class":441,"line":537},[439,821,466],{"class":453},[439,823,542],{"class":469},[439,825,725],{"class":453},[439,827,728],{"class":449},[439,829,700],{"class":453},[439,831,552],{"class":449},[439,833,736],{"class":453},[439,835,501],{"class":453},[439,837,552],{"class":708},[439,839,420],{"class":453},[439,841,842],{"class":469},"length",[439,844,845],{"class":453},"()",[439,847,848],{"class":497}," >",[439,850,561],{"class":519},[439,852,645],{"class":453},[439,854,855,857,859,861,864,866,868,870,873],{"class":441,"line":566},[439,856,466],{"class":453},[439,858,571],{"class":469},[439,860,507],{"class":453},[439,862,863],{"class":449},"value ",[439,865,476],{"class":453},[439,867,552],{"class":708},[439,869,420],{"class":453},[439,871,872],{"class":469},"toUpperCase",[439,874,875],{"class":453},"());\n",[439,877,878],{"class":441,"line":591},[439,879,595],{"emptyLinePlaceholder":594},[439,881,882,885,887,889],{"class":441,"line":598},[439,883,884],{"class":708},"intermediate",[439,886,420],{"class":453},[439,888,604],{"class":469},[439,890,607],{"class":453},[439,892,893,896,899,901],{"class":441,"line":610},[439,894,895],{"class":741},"    \"",[439,897,898],{"class":479},"output-topic",[439,900,748],{"class":741},[439,902,616],{"class":453},[439,904,905,908,910,912,914,916,918,920,923,925,927,930,932],{"class":441,"line":619},[439,906,907],{"class":449},"    Produced",[439,909,420],{"class":453},[439,911,628],{"class":497},[439,913,632],{"class":449},[439,915,700],{"class":453},[439,917,703],{"class":449},[439,919,558],{"class":497},[439,921,922],{"class":469},"as",[439,924,507],{"class":453},[439,926,748],{"class":741},[439,928,929],{"class":479},"sink1",[439,931,748],{"class":741},[439,933,645],{"class":453},[439,935,936,938,940,942,945,947,949],{"class":441,"line":648},[439,937,466],{"class":453},[439,939,653],{"class":469},[439,941,507],{"class":453},[439,943,944],{"class":708},"Serdes",[439,946,420],{"class":453},[439,948,632],{"class":469},[439,950,661],{"class":453},[439,952,953,955,957,959,961,963,965],{"class":441,"line":664},[439,954,466],{"class":453},[439,956,669],{"class":469},[439,958,507],{"class":453},[439,960,944],{"class":708},[439,962,420],{"class":453},[439,964,632],{"class":469},[439,966,967],{"class":453},"()));\n",[320,969,970],{},"Two things shift even on the unchanged-code path:",[327,972,973,991],{},[330,974,975,978,979,982,983,986,987,990],{},[333,976,977],{},"Imports."," StoatFlow's DSL lives under ",[338,980,981],{},"io.stoatflow.core.topology.*"," (and the runtime under ",[338,984,985],{},"io.stoatflow.runtime.*",") rather than ",[338,988,989],{},"org.apache.kafka.streams.*",". This is a package rename across your topology source, not a logic change.",[330,992,993,996,997,1000,1001,1004,1005,1008,1009,1012,1013,1016,1017,1020,1021,1016,1024,1027,1028,1031,1032,420],{},[333,994,995],{},"Entry point."," Where Kafka Streams takes a ",[338,998,999],{},"Topology"," plus a ",[338,1002,1003],{},"Properties"," and a ",[338,1006,1007],{},"KafkaStreams"," object you ",[338,1010,1011],{},"start()",", StoatFlow has two front doors — ",[338,1014,1015],{},"StoatFlow.fromBuilder(config, builder)"," on ",[338,1018,1019],{},":core"," (DSL and engine only) and ",[338,1022,1023],{},"StoatFlowRuntime.fromConfig(...)",[338,1025,1026],{},":runtime"," (the batteries-included wrapper that loads ",[338,1029,1030],{},"application.yaml"," and starts the HTTP admin, metrics, and health endpoints). See ",[371,1033,13],{"href":14},[380,1035,1037],{"id":1036},"what-changes-and-where-it-bites","What changes — and where it bites",[320,1039,1040],{},"The DSL is stable; the engine underneath is a different shape. None of these require topology rewrites, but they change how you configure, deploy, and reason about the application. Each is covered in depth on its concept page — this is the orientation.",[1042,1043,1044,1063],"table",{},[1045,1046,1047],"thead",{},[1048,1049,1050,1054,1057,1060],"tr",{},[1051,1052,1053],"th",{},"Area",[1051,1055,1056],{},"Kafka Streams",[1051,1058,1059],{},"StoatFlow",[1051,1061,1062],{},"Migration impact",[1064,1065,1066,1081,1095],"tbody",{},[1048,1067,1068,1072,1075,1078],{},[1069,1070,1071],"td",{},"Deployment",[1069,1073,1074],{},"Multiple instances, rebalancing consumer group",[1069,1076,1077],{},"One JVM, single-member group, no rebalancing",[1069,1079,1080],{},"You deploy one pod, sized by cores and memory — not a fleet sized by instance count",[1048,1082,1083,1086,1089,1092],{},[1069,1084,1085],{},"Exactly-once",[1069,1087,1088],{},"Per-task transactions, coordinated across the cluster",[1069,1090,1091],{},"One commit barrier → one transaction, whole topology",[1069,1093,1094],{},"EOS is the default, not an opt-in you tune",[1048,1096,1097,1100,1106,1112],{},[1069,1098,1099],{},"Config contract",[1069,1101,1102,1105],{},[338,1103,1104],{},"StreamsConfig"," properties tuned per deployment",[1069,1107,1108,1109,1111],{},"Typed config \u002F ",[338,1110,1030],{},", with KS-specific keys dropped",[1069,1113,1114],{},"Some KS config keys have no meaning here and are removed (below)",[320,1116,1117,1118,1121,1122,1126,1127,1130,1131,420],{},"The same shift runs through parallelism (lane count is a config knob, not a topic-layout decision), re-keying (internal repartition topics disappear — ",[338,1119,1120],{},"repartition()"," becomes in-process), and state (global — no co-partitioning requirement, no partition routing for queries). The full aspect-by-aspect table, and the conceptual ",[1123,1124,1125],"em",{},"why"," behind each row, are on ",[371,1128,82],{"href":1129},"\u002Fdocs\u002Fconcepts\u002Fhow-stoatflow-differs-from-ks#the-deltas-at-a-glance","; the model in its own right is on ",[371,1132,47],{"href":48},[1134,1135,1137],"h3",{"id":1136},"config-keys-that-go-away","Config keys that go away",[320,1139,1140],{},"A subset of Kafka Streams configuration encodes assumptions that the single-instance model removes. These keys have no StoatFlow equivalent and are dropped during the port:",[327,1142,1143,1165,1175],{},[330,1144,1145,1148,1149,1152,1153,1156,1157,1160,1161,1164],{},[333,1146,1147],{},"Instance- and rebalancing-related"," — ",[338,1150,1151],{},"num.stream.threads",", standby-replica settings, static-membership and cooperative-rebalancing tuning. There is no group to rebalance and no second ",[1123,1154,1155],{},"active"," instance, so these KS knobs have nothing to act on. (StoatFlow's own opt-in ",[371,1158,1159],{"href":234},"hot standby"," is a separate mechanism under ",[338,1162,1163],{},"stoatflow.ha.*",", not a port of these.)",[330,1166,1167,1170,1171,1174],{},[333,1168,1169],{},"Exactly-once enablement"," — the KS ",[338,1172,1173],{},"processing.guarantee"," opt-in and its associated transactional tuning. StoatFlow commits the whole topology under one barrier-linked transaction by default; you choose exactly-once or at-least-once as a mode, not by assembling the per-task transactional machinery yourself.",[330,1176,1177,1148,1180,1183,1184,1187,1188,341,1191,1194],{},[333,1178,1179],{},"Partitioning that assumes a cluster",[338,1181,1182],{},"withPartition(...)"," on interactive queries, partitioner settings on ",[338,1185,1186],{},"TableJoined"," (",[338,1189,1190],{},"withPartitioner",[338,1192,1193],{},"withOtherPartitioner","), and the repartition-topic partition counts. With one instance and global state there is nothing to route across.",[1196,1197,1200],"callout",{"color":1198,"icon":1199},"info","i-lucide-info",[320,1201,1202,1203,1205,1206,1209,1210,420],{},"This is removal, not translation: there is no StoatFlow key that \"replaces\" ",[338,1204,1151],{},". Parallelism is expressed as lane count instead. The full key-by-key reference is in the ",[371,1207,1208],{"href":282},"configuration reference","; the model is on ",[371,1211,1212],{"href":73},"Configuration model",[1134,1214,1216],{"id":1215},"api-surface-differences","API-surface differences",[320,1218,1219,1220,1222,1223,1226,1227,1016,1230,1232,1233,1236,1237,1240,1241,1244,1245,1247],{},"A handful of Kafka Streams APIs deliberately diverge. Some are accepted but inert because the single-instance model gives them nothing to act on — the partitioner methods on ",[338,1221,1186],{},", for example, compile and do nothing. Some are stricter than KS — a configured ",[338,1224,1225],{},"processor.wrapper.class"," throws instead of being silently ignored, and a multicast ",[338,1228,1229],{},"StreamPartitioner",[338,1231,1120],{}," is rejected. And a few have small shape changes — ",[338,1234,1235],{},"VersionedRecord.validTo()"," returns an ",[338,1238,1239],{},"Optional",", exception handlers receive a ",[338,1242,1243],{},"Record"," instead of separate key\u002Fvalue. These are catalogued in the ",[371,1246,419],{"href":302},"; the shape changes surface at compile time during the port, which makes them easy to find and fix.",[380,1249,1251],{"id":1250},"the-decision-green-field-cutover-vs-carrying-state","The decision: green-field cutover vs. carrying state",[320,1253,1254,1255,420],{},"The single question that determines the shape of your migration is ",[333,1256,1257],{},"what happens to your existing state",[320,1259,1260],{},"StoatFlow does not read Kafka Streams' changelog topics directly. The two engines store and lay out state differently, so there is no in-place restore from a KS changelog into a StoatFlow store. That leaves two honest paths, and which one you take depends entirely on whether your topology's correctness depends on state that you cannot cheaply rebuild.",[320,1262,1263],{},"The decision, compressed into one tree:",[429,1265,1269],{"className":1266,"code":1267,"language":1268,"meta":435,"style":435},"language-mermaid shiki shiki-themes vitesse-light","flowchart TD\n    Q1{\"Is the topology stateful?\"}\n    Q1 -->|\"no — map \u002F filter \u002F route\"| A1[\"Straight cutover:\u003Cbr\u002F>fresh application id,\u003Cbr\u002F>reprocess or start from latest\"]\n    Q1 -->|yes| Q2{\"Can the state be rebuilt\u003Cbr\u002F>by reprocessing?\u003Cbr\u002F>(source retention covers it,\u003Cbr\u002F>catch-up window acceptable)\"}\n    Q2 -->|yes| A2[\"Rebuild by reprocessing:\u003Cbr\u002F>fresh application id, earliest,\u003Cbr\u002F>validate, then switch traffic\"]\n    Q2 -->|no| A3[\"State-carrying plan:\u003Cbr\u002F>run alongside, validate\u003Cbr\u002F>before cutover — talk to us\"]\n    A1 --> P1([\"Without data migration\"])\n    A2 --> P1\n    A3 --> P2([\"With data migration\"])\n","mermaid",[338,1270,1271,1276,1281,1286,1291,1296,1301,1306,1311],{"__ignoreMap":435},[439,1272,1273],{"class":441,"line":442},[439,1274,1275],{"class":449},"flowchart TD\n",[439,1277,1278],{"class":441,"line":457},[439,1279,1280],{"class":449},"    Q1{\"Is the topology stateful?\"}\n",[439,1282,1283],{"class":441,"line":463},[439,1284,1285],{"class":449},"    Q1 -->|\"no — map \u002F filter \u002F route\"| A1[\"Straight cutover:\u003Cbr\u002F>fresh application id,\u003Cbr\u002F>reprocess or start from latest\"]\n",[439,1287,1288],{"class":441,"line":486},[439,1289,1290],{"class":449},"    Q1 -->|yes| Q2{\"Can the state be rebuilt\u003Cbr\u002F>by reprocessing?\u003Cbr\u002F>(source retention covers it,\u003Cbr\u002F>catch-up window acceptable)\"}\n",[439,1292,1293],{"class":441,"line":537},[439,1294,1295],{"class":449},"    Q2 -->|yes| A2[\"Rebuild by reprocessing:\u003Cbr\u002F>fresh application id, earliest,\u003Cbr\u002F>validate, then switch traffic\"]\n",[439,1297,1298],{"class":441,"line":566},[439,1299,1300],{"class":449},"    Q2 -->|no| A3[\"State-carrying plan:\u003Cbr\u002F>run alongside, validate\u003Cbr\u002F>before cutover — talk to us\"]\n",[439,1302,1303],{"class":441,"line":591},[439,1304,1305],{"class":449},"    A1 --> P1([\"Without data migration\"])\n",[439,1307,1308],{"class":441,"line":598},[439,1309,1310],{"class":449},"    A2 --> P1\n",[439,1312,1313],{"class":441,"line":610},[439,1314,1315],{"class":449},"    A3 --> P2([\"With data migration\"])\n",[1317,1318,1319,1325],"path-cards",{},[1320,1321,1322],"path-card",{"icon":267,"title":373,"to":265},[320,1323,1324],{},"Green-field cutover. Your topology is stateless, or its state can be rebuilt by reprocessing the input topics from the beginning. Point StoatFlow at the source topics, let it reprocess, and cut over. The simplest path — pick it whenever you can.",[1320,1326,1327],{"icon":103,"title":377,"to":270},[320,1328,1329],{},"State-carrying migration. Your topology accumulates state you cannot afford to rebuild — input topics have aged out of retention, or a full reprocess is too slow or too expensive. What carrying state means in practice: what StoatFlow's restoration can reuse, why Kafka Streams changelogs can't be imported directly, and how to plan the rebuild and cutover.",[1134,1331,1333],{"id":1332},"when-a-green-field-cutover-is-the-right-call","When a green-field cutover is the right call",[320,1335,1336,1337,1339,1340,1343],{},"Choose ",[371,1338,373],{"href":265}," when ",[333,1341,1342],{},"any"," of these holds:",[327,1345,1346,1360,1367],{},[330,1347,1348,1349,1148,1352,341,1354,341,1356,1359],{},"The topology is ",[333,1350,1351],{},"stateless",[338,1353,491],{},[338,1355,542],{},[338,1357,1358],{},"flatMap"," \u002F routing, no aggregations or joins that accumulate. There is nothing to carry; reprocessing produces identical output.",[330,1361,1362,1363,1366],{},"The state is ",[333,1364,1365],{},"derivable from input still in retention."," If your source topics retain enough history to recompute every aggregate, count, and join from scratch, reprocessing rebuilds the state exactly, and the cutover is a config-and-deploy exercise.",[330,1368,1369,1370,1373],{},"You can tolerate a ",[333,1371,1372],{},"reprocessing window."," Reading the input topics from the beginning takes time proportional to their size; if you can run the new instance to catch-up before switching consumers over, this is the lowest-risk path.",[320,1375,1376],{},"This is the default recommendation. It avoids any state-format coupling between the two engines, and the result is provably correct because it is computed from the same input the KS app consumed.",[1134,1378,1380],{"id":1379},"when-you-need-a-state-carrying-migration","When you need a state-carrying migration",[320,1382,1336,1383,1385],{},[371,1384,377],{"href":270}," when reprocessing is not viable:",[327,1387,1388,1394,1400],{},[330,1389,1390,1393],{},[333,1391,1392],{},"Input topics have aged out."," Compacted or time-limited source topics no longer hold the full history, so a reprocess would produce incomplete aggregates.",[330,1395,1396,1399],{},[333,1397,1398],{},"A full reprocess is too slow or too expensive."," Very large state, or long-running windowed aggregations spanning weeks, can make recomputation impractical even when the input technically survives.",[330,1401,1402,1405],{},[333,1403,1404],{},"You need continuity of in-flight state"," — open sessions, long windows, running counters that downstream systems depend on without a recomputation gap.",[320,1407,1408],{},"This path is more constrained than the name suggests: StoatFlow cannot import Kafka Streams changelogs or store files directly, so \"carrying state\" is a per-topology plan rather than a switch you flip. The sub-page covers what StoatFlow's own restoration can and cannot reuse, why direct changelog reuse is off the table, and how to sequence the rebuild — and migrations of this shape are worth talking through with us.",[1196,1410,1412],{"color":1198,"icon":1411},"i-lucide-message-circle",[320,1413,1414,1415,1419],{},"Not sure which path your topology falls into? The deciding factor is almost always retention versus state size. ",[371,1416,1418],{"href":1417},"\u002Fcontact","Get in touch"," — we'll work through the trade-off with you before you commit to a path.",[380,1421,1423],{"id":1422},"recommended-order","Recommended order",[320,1425,1426],{},"A migration in practice tends to run in this order, regardless of which state path you take:",[1428,1429,1430,1440,1448,1461],"ol",{},[330,1431,1432,1435,1436,1439],{},[333,1433,1434],{},"Port the build and the topology."," Swap the dependency, rename the imports, change the entry point, and let the compiler surface the dropped APIs. Validate the ported topology with the in-memory ",[371,1437,1438],{"href":106},"test driver"," — no broker required, deterministic, and the fastest way to confirm the logic is intact.",[330,1441,1442,1445,1446,420],{},[333,1443,1444],{},"Clean up the config."," Remove the KS keys that have no meaning here (above) and express parallelism as lane count. See ",[371,1447,1212],{"href":73},[330,1449,1450,1148,1453,1456,1457,1460],{},[333,1451,1452],{},"Pick the state path",[371,1454,1455],{"href":265},"without"," or ",[371,1458,1459],{"href":270},"with"," data migration — and follow that page.",[330,1462,1463,1466,1467,1470,1471,420],{},[333,1464,1465],{},"Deploy as a single pod"," and wire up the operational surface: health probes, metrics, and the debug endpoints described on ",[371,1468,47],{"href":1469},"\u002Fdocs\u002Fconcepts\u002Farchitecture#failure-modes-and-observability"," and in ",[371,1472,1473],{"href":221},"Operating",[380,1475,1477],{"id":1476},"where-to-go-next","Where to go next",[327,1479,1480,1485,1490,1495,1502,1507],{},[330,1481,1482,1484],{},[371,1483,373],{"href":265}," — the green-field cutover path: reprocess from input, no state carried across",[330,1486,1487,1489],{},[371,1488,377],{"href":270}," — carrying existing state across when reprocessing isn't viable",[330,1491,1492,1494],{},[371,1493,82],{"href":83}," — the conceptual deltas the migration follows from",[330,1496,1497,1501],{},[371,1498,1500],{"href":1499},"\u002Fproduct\u002Fcomparison-matrix","Comparison matrix"," — feature-by-feature against Kafka Streams and Flink",[330,1503,1504,1506],{},[371,1505,419],{"href":302}," — method-by-method DSL parity and the deliberate exceptions",[330,1508,1509,1510,1512],{},"Migration in flight and want a second pair of eyes? ",[371,1511,1418],{"href":1417}," — real people read every email during the alpha.",[1514,1515,1516],"style",{},"html pre.shiki code .sbBg2, html code.shiki .sbBg2{--shiki-default:#1E754F}html pre.shiki code .suHK_, html code.shiki .suHK_{--shiki-default:#393A34}html pre.shiki code .sYZai, html code.shiki .sYZai{--shiki-default:#999999}html pre.shiki code .sySUi, html code.shiki .sySUi{--shiki-default:#59873A}html pre.shiki code .spphp, html code.shiki .spphp{--shiki-default:#B56959}html pre.shiki code .si04Y, html code.shiki .si04Y{--shiki-default:#AB5959}html pre.shiki code .s-TwI, html code.shiki .s-TwI{--shiki-default:#2F798A}html pre.shiki code .sUxyF, html code.shiki .sUxyF{--shiki-default:#2E8F82}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 pre.shiki code .svycV, html code.shiki .svycV{--shiki-default:#B07D48}html pre.shiki code .sSP4y, html code.shiki .sSP4y{--shiki-default:#B5695977}",{"title":435,"searchDepth":457,"depth":457,"links":1518},[1519,1520,1524,1528,1529],{"id":382,"depth":457,"text":383},{"id":1036,"depth":457,"text":1037,"children":1521},[1522,1523],{"id":1136,"depth":463,"text":1137},{"id":1215,"depth":463,"text":1216},{"id":1250,"depth":457,"text":1251,"children":1525},[1526,1527],{"id":1332,"depth":463,"text":1333},{"id":1379,"depth":463,"text":1380},{"id":1422,"depth":457,"text":1423},{"id":1476,"depth":457,"text":1477},"What carries over unchanged when you move a Kafka Streams topology to StoatFlow, what changes operationally, and how to decide between a green-field cutover and a state-carrying migration.","md",{},{"icon":85},"explanation",{"title":258,"description":1530},"lFM9-tFZXxyThiU41xYkxwtF18nfu3-v8VfkWGun5Eo",[1538,1540],{"title":253,"path":254,"stem":255,"description":1539,"icon":256,"children":-1},"A go-live checklist for StoatFlow — replica count, license, probes, metrics, error policies, tuning, and thread safety — each item linking to the canonical page that covers it in full.",{"title":264,"path":265,"stem":266,"description":1541,"icon":267,"children":-1},"Green-field cutover from Kafka Streams — point StoatFlow at the same source topics with a fresh consumer group, let stateful operators rebuild from changelog\u002Fsource, then switch traffic. The dependency, build, and config swap, with the before\u002Fafter grounded in the map-filter example.",1783713350445]