[{"data":1,"prerenderedAt":3131},["ShallowReactive",2],{"article-en-mvcc-transactions-in-blite":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"tags":11,"body":20,"_type":3124,"_id":3125,"_source":3126,"_file":3127,"_stem":3128,"_extension":3129,"sitemap":3130},"\u002Fen\u002Fblog\u002Fmvcc-transactions-in-blite","blog",false,"","MVCC Lite: How BLite Gives You Transactions Without a Full Concurrency Engine","How BLite implements transaction isolation using two in-memory dictionaries — a WAL cache for uncommitted writes and a WAL index for committed-but-not-checkpointed pages — and where that falls short of true MVCC.","2026-03-31",[12,13,14,15,16,17,18,19],".net","blite","mvcc","transactions","concurrency","acid","open-source","internals",{"type":21,"children":22,"toc":3112},"root",[23,31,36,40,47,60,65,70,73,79,84,258,282,299,304,660,679,682,688,699,864,874,1178,1203,1520,1537,1630,1635,1638,1656,1661,2311,2323,2326,2340,2352,2524,2549,2552,2558,2577,2924,2943,2946,2952,2957,3009,3014,3031,3055,3072,3075,3081,3086,3091,3106],{"type":24,"tag":25,"props":26,"children":27},"element","p",{},[28],{"type":29,"value":30},"text","Every serious database makes the same promise: a transaction either commits fully or doesn't happen at all. Reading data mid-transaction gives you a consistent snapshot, not a half-written mess. And if the process crashes, you don't lose committed data.",{"type":24,"tag":25,"props":32,"children":33},{},[34],{"type":29,"value":35},"Keeping those promises while allowing concurrent readers and writers is the hard part. Most production databases use Multi-Version Concurrency Control — MVCC — to do it. BLite uses a simpler model that gets most of the guarantees with a fraction of the complexity. Here's exactly how it works and where the cracks are.",{"type":24,"tag":37,"props":38,"children":39},"hr",{},[],{"type":24,"tag":41,"props":42,"children":44},"h2",{"id":43},"the-problem-mvcc-solves",[45],{"type":29,"value":46},"The Problem MVCC Solves",{"type":24,"tag":25,"props":48,"children":49},{},[50,52,58],{"type":29,"value":51},"Without any concurrency control, two transactions reading and writing the same pages simultaneously produce chaos: one transaction reads a page that another is halfway through writing. The fix is to give each transaction its own ",{"type":24,"tag":53,"props":54,"children":55},"em",{},[56],{"type":29,"value":57},"view",{"type":29,"value":59}," of the data — a snapshot taken at the moment the transaction started, immune to concurrent modifications.",{"type":24,"tag":25,"props":61,"children":62},{},[63],{"type":29,"value":64},"True MVCC (as in PostgreSQL) does this with version chains: every updated row keeps its old version on disk alongside the new one, tagged with transaction IDs. Readers pick the version whose timestamp is ≤ their transaction start time. Writers never block readers.",{"type":24,"tag":25,"props":66,"children":67},{},[68],{"type":29,"value":69},"This is powerful and general. It's also complex: vacuum processes, tuple headers, transaction ID wraparound, visibility maps. BLite takes a different approach.",{"type":24,"tag":37,"props":71,"children":72},{},[],{"type":24,"tag":41,"props":74,"children":76},{"id":75},"two-dictionaries-one-contract",[77],{"type":29,"value":78},"Two Dictionaries, One Contract",{"type":24,"tag":25,"props":80,"children":81},{},[82],{"type":29,"value":83},"BLite's transaction isolation is built on two in-memory data structures:",{"type":24,"tag":85,"props":86,"children":90},"pre",{"className":87,"code":88,"language":89,"meta":7,"style":7},"language-csharp shiki shiki-themes one-dark-pro","\u002F\u002F Per-transaction uncommitted writes\nprivate readonly ConcurrentDictionary\u003Culong, ConcurrentDictionary\u003Cuint, byte[]>> _walCache = new();\n\n\u002F\u002F Committed-but-not-yet-checkpointed pages (the \"stable ring\")\nprivate readonly ConcurrentDictionary\u003Cuint, byte[]> _walIndex = new();\n","csharp",[91],{"type":24,"tag":92,"props":93,"children":94},"code",{"__ignoreMap":7},[95,107,189,199,208],{"type":24,"tag":96,"props":97,"children":100},"span",{"class":98,"line":99},"line",1,[101],{"type":24,"tag":96,"props":102,"children":104},{"style":103},"--shiki-default:#7F848E;--shiki-default-font-style:italic",[105],{"type":29,"value":106},"\u002F\u002F Per-transaction uncommitted writes\n",{"type":24,"tag":96,"props":108,"children":110},{"class":98,"line":109},2,[111,117,122,128,134,139,144,149,153,158,162,167,172,178,184],{"type":24,"tag":96,"props":112,"children":114},{"style":113},"--shiki-default:#C678DD",[115],{"type":29,"value":116},"private",{"type":24,"tag":96,"props":118,"children":119},{"style":113},[120],{"type":29,"value":121}," readonly",{"type":24,"tag":96,"props":123,"children":125},{"style":124},"--shiki-default:#E5C07B",[126],{"type":29,"value":127}," ConcurrentDictionary",{"type":24,"tag":96,"props":129,"children":131},{"style":130},"--shiki-default:#ABB2BF",[132],{"type":29,"value":133},"\u003C",{"type":24,"tag":96,"props":135,"children":136},{"style":113},[137],{"type":29,"value":138},"ulong",{"type":24,"tag":96,"props":140,"children":141},{"style":130},[142],{"type":29,"value":143},", ",{"type":24,"tag":96,"props":145,"children":146},{"style":124},[147],{"type":29,"value":148},"ConcurrentDictionary",{"type":24,"tag":96,"props":150,"children":151},{"style":130},[152],{"type":29,"value":133},{"type":24,"tag":96,"props":154,"children":155},{"style":113},[156],{"type":29,"value":157},"uint",{"type":24,"tag":96,"props":159,"children":160},{"style":130},[161],{"type":29,"value":143},{"type":24,"tag":96,"props":163,"children":164},{"style":113},[165],{"type":29,"value":166},"byte",{"type":24,"tag":96,"props":168,"children":169},{"style":130},[170],{"type":29,"value":171},"[]>> ",{"type":24,"tag":96,"props":173,"children":175},{"style":174},"--shiki-default:#E06C75",[176],{"type":29,"value":177},"_walCache",{"type":24,"tag":96,"props":179,"children":181},{"style":180},"--shiki-default:#56B6C2",[182],{"type":29,"value":183}," =",{"type":24,"tag":96,"props":185,"children":186},{"style":130},[187],{"type":29,"value":188}," new();\n",{"type":24,"tag":96,"props":190,"children":192},{"class":98,"line":191},3,[193],{"type":24,"tag":96,"props":194,"children":196},{"emptyLinePlaceholder":195},true,[197],{"type":29,"value":198},"\n",{"type":24,"tag":96,"props":200,"children":202},{"class":98,"line":201},4,[203],{"type":24,"tag":96,"props":204,"children":205},{"style":103},[206],{"type":29,"value":207},"\u002F\u002F Committed-but-not-yet-checkpointed pages (the \"stable ring\")\n",{"type":24,"tag":96,"props":209,"children":211},{"class":98,"line":210},5,[212,216,220,224,228,232,236,240,245,250,254],{"type":24,"tag":96,"props":213,"children":214},{"style":113},[215],{"type":29,"value":116},{"type":24,"tag":96,"props":217,"children":218},{"style":113},[219],{"type":29,"value":121},{"type":24,"tag":96,"props":221,"children":222},{"style":124},[223],{"type":29,"value":127},{"type":24,"tag":96,"props":225,"children":226},{"style":130},[227],{"type":29,"value":133},{"type":24,"tag":96,"props":229,"children":230},{"style":113},[231],{"type":29,"value":157},{"type":24,"tag":96,"props":233,"children":234},{"style":130},[235],{"type":29,"value":143},{"type":24,"tag":96,"props":237,"children":238},{"style":113},[239],{"type":29,"value":166},{"type":24,"tag":96,"props":241,"children":242},{"style":130},[243],{"type":29,"value":244},"[]> ",{"type":24,"tag":96,"props":246,"children":247},{"style":174},[248],{"type":29,"value":249},"_walIndex",{"type":24,"tag":96,"props":251,"children":252},{"style":180},[253],{"type":29,"value":183},{"type":24,"tag":96,"props":255,"children":256},{"style":130},[257],{"type":29,"value":188},{"type":24,"tag":25,"props":259,"children":260},{},[261,266,268,273,275,280],{"type":24,"tag":92,"props":262,"children":264},{"className":263},[],[265],{"type":29,"value":177},{"type":29,"value":267}," is a dictionary of dictionaries: the outer key is the transaction ID (",{"type":24,"tag":92,"props":269,"children":271},{"className":270},[],[272],{"type":29,"value":138},{"type":29,"value":274},"), the inner key is the page ID (",{"type":24,"tag":92,"props":276,"children":278},{"className":277},[],[279],{"type":29,"value":157},{"type":29,"value":281},"), and the value is the page's byte array. Every write goes into the caller's private cache, invisible to anyone else.",{"type":24,"tag":25,"props":283,"children":284},{},[285,290,292,297],{"type":24,"tag":92,"props":286,"children":288},{"className":287},[],[289],{"type":29,"value":249},{"type":29,"value":291}," is a flat dictionary: page ID → current byte array. It represents the committed state of all pages that have been written since the last checkpoint. When you read a page, you first check ",{"type":24,"tag":92,"props":293,"children":295},{"className":294},[],[296],{"type":29,"value":249},{"type":29,"value":298}," for the committed version.",{"type":24,"tag":25,"props":300,"children":301},{},[302],{"type":29,"value":303},"The read path combines both:",{"type":24,"tag":85,"props":305,"children":307},{"className":87,"code":306,"language":89,"meta":7,"style":7},"public byte[] ReadPage(uint pageId, ulong transactionId)\n{\n    \u002F\u002F First: see if this transaction has written the page (read your own writes)\n    if (_walCache.TryGetValue(transactionId, out var txnCache) &&\n        txnCache.TryGetValue(pageId, out var uncommitted))\n        return uncommitted;\n\n    \u002F\u002F Second: see if there's a committed version since last checkpoint\n    if (_walIndex.TryGetValue(pageId, out var committed))\n        return committed;\n\n    \u002F\u002F Third: read from the durably persisted page file\n    return _pageFile.ReadPage(pageId);\n}\n",[308],{"type":24,"tag":92,"props":309,"children":310},{"__ignoreMap":7},[311,367,375,383,448,495,513,521,530,583,599,607,616,651],{"type":24,"tag":96,"props":312,"children":313},{"class":98,"line":99},[314,319,324,329,335,340,344,349,353,357,362],{"type":24,"tag":96,"props":315,"children":316},{"style":113},[317],{"type":29,"value":318},"public",{"type":24,"tag":96,"props":320,"children":321},{"style":113},[322],{"type":29,"value":323}," byte",{"type":24,"tag":96,"props":325,"children":326},{"style":130},[327],{"type":29,"value":328},"[] ",{"type":24,"tag":96,"props":330,"children":332},{"style":331},"--shiki-default:#61AFEF",[333],{"type":29,"value":334},"ReadPage",{"type":24,"tag":96,"props":336,"children":337},{"style":130},[338],{"type":29,"value":339},"(",{"type":24,"tag":96,"props":341,"children":342},{"style":113},[343],{"type":29,"value":157},{"type":24,"tag":96,"props":345,"children":346},{"style":124},[347],{"type":29,"value":348}," pageId",{"type":24,"tag":96,"props":350,"children":351},{"style":130},[352],{"type":29,"value":143},{"type":24,"tag":96,"props":354,"children":355},{"style":113},[356],{"type":29,"value":138},{"type":24,"tag":96,"props":358,"children":359},{"style":124},[360],{"type":29,"value":361}," transactionId",{"type":24,"tag":96,"props":363,"children":364},{"style":130},[365],{"type":29,"value":366},")\n",{"type":24,"tag":96,"props":368,"children":369},{"class":98,"line":109},[370],{"type":24,"tag":96,"props":371,"children":372},{"style":130},[373],{"type":29,"value":374},"{\n",{"type":24,"tag":96,"props":376,"children":377},{"class":98,"line":191},[378],{"type":24,"tag":96,"props":379,"children":380},{"style":103},[381],{"type":29,"value":382},"    \u002F\u002F First: see if this transaction has written the page (read your own writes)\n",{"type":24,"tag":96,"props":384,"children":385},{"class":98,"line":201},[386,391,396,400,405,410,414,419,423,428,433,438,443],{"type":24,"tag":96,"props":387,"children":388},{"style":113},[389],{"type":29,"value":390},"    if",{"type":24,"tag":96,"props":392,"children":393},{"style":130},[394],{"type":29,"value":395}," (",{"type":24,"tag":96,"props":397,"children":398},{"style":124},[399],{"type":29,"value":177},{"type":24,"tag":96,"props":401,"children":402},{"style":130},[403],{"type":29,"value":404},".",{"type":24,"tag":96,"props":406,"children":407},{"style":331},[408],{"type":29,"value":409},"TryGetValue",{"type":24,"tag":96,"props":411,"children":412},{"style":130},[413],{"type":29,"value":339},{"type":24,"tag":96,"props":415,"children":416},{"style":174},[417],{"type":29,"value":418},"transactionId",{"type":24,"tag":96,"props":420,"children":421},{"style":130},[422],{"type":29,"value":143},{"type":24,"tag":96,"props":424,"children":425},{"style":113},[426],{"type":29,"value":427},"out",{"type":24,"tag":96,"props":429,"children":430},{"style":113},[431],{"type":29,"value":432}," var",{"type":24,"tag":96,"props":434,"children":435},{"style":174},[436],{"type":29,"value":437}," txnCache",{"type":24,"tag":96,"props":439,"children":440},{"style":130},[441],{"type":29,"value":442},") ",{"type":24,"tag":96,"props":444,"children":445},{"style":180},[446],{"type":29,"value":447},"&&\n",{"type":24,"tag":96,"props":449,"children":450},{"class":98,"line":210},[451,456,460,464,468,473,477,481,485,490],{"type":24,"tag":96,"props":452,"children":453},{"style":124},[454],{"type":29,"value":455},"        txnCache",{"type":24,"tag":96,"props":457,"children":458},{"style":130},[459],{"type":29,"value":404},{"type":24,"tag":96,"props":461,"children":462},{"style":331},[463],{"type":29,"value":409},{"type":24,"tag":96,"props":465,"children":466},{"style":130},[467],{"type":29,"value":339},{"type":24,"tag":96,"props":469,"children":470},{"style":174},[471],{"type":29,"value":472},"pageId",{"type":24,"tag":96,"props":474,"children":475},{"style":130},[476],{"type":29,"value":143},{"type":24,"tag":96,"props":478,"children":479},{"style":113},[480],{"type":29,"value":427},{"type":24,"tag":96,"props":482,"children":483},{"style":113},[484],{"type":29,"value":432},{"type":24,"tag":96,"props":486,"children":487},{"style":174},[488],{"type":29,"value":489}," uncommitted",{"type":24,"tag":96,"props":491,"children":492},{"style":130},[493],{"type":29,"value":494},"))\n",{"type":24,"tag":96,"props":496,"children":498},{"class":98,"line":497},6,[499,504,508],{"type":24,"tag":96,"props":500,"children":501},{"style":113},[502],{"type":29,"value":503},"        return",{"type":24,"tag":96,"props":505,"children":506},{"style":174},[507],{"type":29,"value":489},{"type":24,"tag":96,"props":509,"children":510},{"style":130},[511],{"type":29,"value":512},";\n",{"type":24,"tag":96,"props":514,"children":516},{"class":98,"line":515},7,[517],{"type":24,"tag":96,"props":518,"children":519},{"emptyLinePlaceholder":195},[520],{"type":29,"value":198},{"type":24,"tag":96,"props":522,"children":524},{"class":98,"line":523},8,[525],{"type":24,"tag":96,"props":526,"children":527},{"style":103},[528],{"type":29,"value":529},"    \u002F\u002F Second: see if there's a committed version since last checkpoint\n",{"type":24,"tag":96,"props":531,"children":533},{"class":98,"line":532},9,[534,538,542,546,550,554,558,562,566,570,574,579],{"type":24,"tag":96,"props":535,"children":536},{"style":113},[537],{"type":29,"value":390},{"type":24,"tag":96,"props":539,"children":540},{"style":130},[541],{"type":29,"value":395},{"type":24,"tag":96,"props":543,"children":544},{"style":124},[545],{"type":29,"value":249},{"type":24,"tag":96,"props":547,"children":548},{"style":130},[549],{"type":29,"value":404},{"type":24,"tag":96,"props":551,"children":552},{"style":331},[553],{"type":29,"value":409},{"type":24,"tag":96,"props":555,"children":556},{"style":130},[557],{"type":29,"value":339},{"type":24,"tag":96,"props":559,"children":560},{"style":174},[561],{"type":29,"value":472},{"type":24,"tag":96,"props":563,"children":564},{"style":130},[565],{"type":29,"value":143},{"type":24,"tag":96,"props":567,"children":568},{"style":113},[569],{"type":29,"value":427},{"type":24,"tag":96,"props":571,"children":572},{"style":113},[573],{"type":29,"value":432},{"type":24,"tag":96,"props":575,"children":576},{"style":174},[577],{"type":29,"value":578}," committed",{"type":24,"tag":96,"props":580,"children":581},{"style":130},[582],{"type":29,"value":494},{"type":24,"tag":96,"props":584,"children":586},{"class":98,"line":585},10,[587,591,595],{"type":24,"tag":96,"props":588,"children":589},{"style":113},[590],{"type":29,"value":503},{"type":24,"tag":96,"props":592,"children":593},{"style":174},[594],{"type":29,"value":578},{"type":24,"tag":96,"props":596,"children":597},{"style":130},[598],{"type":29,"value":512},{"type":24,"tag":96,"props":600,"children":602},{"class":98,"line":601},11,[603],{"type":24,"tag":96,"props":604,"children":605},{"emptyLinePlaceholder":195},[606],{"type":29,"value":198},{"type":24,"tag":96,"props":608,"children":610},{"class":98,"line":609},12,[611],{"type":24,"tag":96,"props":612,"children":613},{"style":103},[614],{"type":29,"value":615},"    \u002F\u002F Third: read from the durably persisted page file\n",{"type":24,"tag":96,"props":617,"children":619},{"class":98,"line":618},13,[620,625,630,634,638,642,646],{"type":24,"tag":96,"props":621,"children":622},{"style":113},[623],{"type":29,"value":624},"    return",{"type":24,"tag":96,"props":626,"children":627},{"style":124},[628],{"type":29,"value":629}," _pageFile",{"type":24,"tag":96,"props":631,"children":632},{"style":130},[633],{"type":29,"value":404},{"type":24,"tag":96,"props":635,"children":636},{"style":331},[637],{"type":29,"value":334},{"type":24,"tag":96,"props":639,"children":640},{"style":130},[641],{"type":29,"value":339},{"type":24,"tag":96,"props":643,"children":644},{"style":174},[645],{"type":29,"value":472},{"type":24,"tag":96,"props":647,"children":648},{"style":130},[649],{"type":29,"value":650},");\n",{"type":24,"tag":96,"props":652,"children":654},{"class":98,"line":653},14,[655],{"type":24,"tag":96,"props":656,"children":657},{"style":130},[658],{"type":29,"value":659},"}\n",{"type":24,"tag":25,"props":661,"children":662},{},[663,665,670,672,677],{"type":29,"value":664},"A transaction always reads its own uncommitted writes. If it hasn't touched a page, it reads from ",{"type":24,"tag":92,"props":666,"children":668},{"className":667},[],[669],{"type":29,"value":249},{"type":29,"value":671}," — the committed state from previous (committed) transactions. If ",{"type":24,"tag":92,"props":673,"children":675},{"className":674},[],[676],{"type":29,"value":249},{"type":29,"value":678}," doesn't have the page either, it falls through to the durable page file.",{"type":24,"tag":37,"props":680,"children":681},{},[],{"type":24,"tag":41,"props":683,"children":685},{"id":684},"transaction-lifecycle",[686],{"type":29,"value":687},"Transaction Lifecycle",{"type":24,"tag":25,"props":689,"children":690},{},[691,697],{"type":24,"tag":692,"props":693,"children":694},"strong",{},[695],{"type":29,"value":696},"Begin:",{"type":29,"value":698}," Just allocate a new transaction ID. Nothing else happens yet.",{"type":24,"tag":85,"props":700,"children":702},{"className":87,"code":701,"language":89,"meta":7,"style":7},"public ulong BeginTransaction()\n{\n    ulong txnId = Interlocked.Increment(ref _transactionCounter);\n    _walCache[txnId] = new ConcurrentDictionary\u003Cuint, byte[]>();\n    return txnId;\n}\n",[703],{"type":24,"tag":92,"props":704,"children":705},{"__ignoreMap":7},[706,728,735,784,842,857],{"type":24,"tag":96,"props":707,"children":708},{"class":98,"line":99},[709,713,718,723],{"type":24,"tag":96,"props":710,"children":711},{"style":113},[712],{"type":29,"value":318},{"type":24,"tag":96,"props":714,"children":715},{"style":113},[716],{"type":29,"value":717}," ulong",{"type":24,"tag":96,"props":719,"children":720},{"style":331},[721],{"type":29,"value":722}," BeginTransaction",{"type":24,"tag":96,"props":724,"children":725},{"style":130},[726],{"type":29,"value":727},"()\n",{"type":24,"tag":96,"props":729,"children":730},{"class":98,"line":109},[731],{"type":24,"tag":96,"props":732,"children":733},{"style":130},[734],{"type":29,"value":374},{"type":24,"tag":96,"props":736,"children":737},{"class":98,"line":191},[738,743,748,752,757,761,766,770,775,780],{"type":24,"tag":96,"props":739,"children":740},{"style":113},[741],{"type":29,"value":742},"    ulong",{"type":24,"tag":96,"props":744,"children":745},{"style":174},[746],{"type":29,"value":747}," txnId",{"type":24,"tag":96,"props":749,"children":750},{"style":180},[751],{"type":29,"value":183},{"type":24,"tag":96,"props":753,"children":754},{"style":124},[755],{"type":29,"value":756}," Interlocked",{"type":24,"tag":96,"props":758,"children":759},{"style":130},[760],{"type":29,"value":404},{"type":24,"tag":96,"props":762,"children":763},{"style":331},[764],{"type":29,"value":765},"Increment",{"type":24,"tag":96,"props":767,"children":768},{"style":130},[769],{"type":29,"value":339},{"type":24,"tag":96,"props":771,"children":772},{"style":113},[773],{"type":29,"value":774},"ref",{"type":24,"tag":96,"props":776,"children":777},{"style":174},[778],{"type":29,"value":779}," _transactionCounter",{"type":24,"tag":96,"props":781,"children":782},{"style":130},[783],{"type":29,"value":650},{"type":24,"tag":96,"props":785,"children":786},{"class":98,"line":201},[787,792,797,802,807,812,817,821,825,829,833,837],{"type":24,"tag":96,"props":788,"children":789},{"style":124},[790],{"type":29,"value":791},"    _walCache",{"type":24,"tag":96,"props":793,"children":794},{"style":130},[795],{"type":29,"value":796},"[",{"type":24,"tag":96,"props":798,"children":799},{"style":174},[800],{"type":29,"value":801},"txnId",{"type":24,"tag":96,"props":803,"children":804},{"style":130},[805],{"type":29,"value":806},"] ",{"type":24,"tag":96,"props":808,"children":809},{"style":180},[810],{"type":29,"value":811},"=",{"type":24,"tag":96,"props":813,"children":814},{"style":130},[815],{"type":29,"value":816}," new ",{"type":24,"tag":96,"props":818,"children":819},{"style":124},[820],{"type":29,"value":148},{"type":24,"tag":96,"props":822,"children":823},{"style":130},[824],{"type":29,"value":133},{"type":24,"tag":96,"props":826,"children":827},{"style":113},[828],{"type":29,"value":157},{"type":24,"tag":96,"props":830,"children":831},{"style":130},[832],{"type":29,"value":143},{"type":24,"tag":96,"props":834,"children":835},{"style":113},[836],{"type":29,"value":166},{"type":24,"tag":96,"props":838,"children":839},{"style":130},[840],{"type":29,"value":841},"[]>();\n",{"type":24,"tag":96,"props":843,"children":844},{"class":98,"line":210},[845,849,853],{"type":24,"tag":96,"props":846,"children":847},{"style":113},[848],{"type":29,"value":624},{"type":24,"tag":96,"props":850,"children":851},{"style":174},[852],{"type":29,"value":747},{"type":24,"tag":96,"props":854,"children":855},{"style":130},[856],{"type":29,"value":512},{"type":24,"tag":96,"props":858,"children":859},{"class":98,"line":497},[860],{"type":24,"tag":96,"props":861,"children":862},{"style":130},[863],{"type":29,"value":659},{"type":24,"tag":25,"props":865,"children":866},{},[867,872],{"type":24,"tag":692,"props":868,"children":869},{},[870],{"type":29,"value":871},"Write:",{"type":29,"value":873}," Buffer the modified page in the transaction's private cache.",{"type":24,"tag":85,"props":875,"children":877},{"className":87,"code":876,"language":89,"meta":7,"style":7},"public void WritePage(uint pageId, byte[] data, ulong transactionId)\n{\n    if (!_walCache.TryGetValue(transactionId, out var txnCache))\n        throw new InvalidOperationException(\"No active transaction\");\n\n    \u002F\u002F Copy so the caller can reuse their buffer\n    var copy = new byte[data.Length];\n    data.CopyTo(copy, 0);\n    txnCache[pageId] = copy;\n}\n",[878],{"type":24,"tag":92,"props":879,"children":880},{"__ignoreMap":7},[881,943,950,1006,1037,1044,1052,1099,1139,1171],{"type":24,"tag":96,"props":882,"children":883},{"class":98,"line":99},[884,888,893,898,902,906,910,914,918,922,927,931,935,939],{"type":24,"tag":96,"props":885,"children":886},{"style":113},[887],{"type":29,"value":318},{"type":24,"tag":96,"props":889,"children":890},{"style":113},[891],{"type":29,"value":892}," void",{"type":24,"tag":96,"props":894,"children":895},{"style":331},[896],{"type":29,"value":897}," WritePage",{"type":24,"tag":96,"props":899,"children":900},{"style":130},[901],{"type":29,"value":339},{"type":24,"tag":96,"props":903,"children":904},{"style":113},[905],{"type":29,"value":157},{"type":24,"tag":96,"props":907,"children":908},{"style":124},[909],{"type":29,"value":348},{"type":24,"tag":96,"props":911,"children":912},{"style":130},[913],{"type":29,"value":143},{"type":24,"tag":96,"props":915,"children":916},{"style":113},[917],{"type":29,"value":166},{"type":24,"tag":96,"props":919,"children":920},{"style":130},[921],{"type":29,"value":328},{"type":24,"tag":96,"props":923,"children":924},{"style":124},[925],{"type":29,"value":926},"data",{"type":24,"tag":96,"props":928,"children":929},{"style":130},[930],{"type":29,"value":143},{"type":24,"tag":96,"props":932,"children":933},{"style":113},[934],{"type":29,"value":138},{"type":24,"tag":96,"props":936,"children":937},{"style":124},[938],{"type":29,"value":361},{"type":24,"tag":96,"props":940,"children":941},{"style":130},[942],{"type":29,"value":366},{"type":24,"tag":96,"props":944,"children":945},{"class":98,"line":109},[946],{"type":24,"tag":96,"props":947,"children":948},{"style":130},[949],{"type":29,"value":374},{"type":24,"tag":96,"props":951,"children":952},{"class":98,"line":191},[953,957,961,966,970,974,978,982,986,990,994,998,1002],{"type":24,"tag":96,"props":954,"children":955},{"style":113},[956],{"type":29,"value":390},{"type":24,"tag":96,"props":958,"children":959},{"style":130},[960],{"type":29,"value":395},{"type":24,"tag":96,"props":962,"children":963},{"style":180},[964],{"type":29,"value":965},"!",{"type":24,"tag":96,"props":967,"children":968},{"style":124},[969],{"type":29,"value":177},{"type":24,"tag":96,"props":971,"children":972},{"style":130},[973],{"type":29,"value":404},{"type":24,"tag":96,"props":975,"children":976},{"style":331},[977],{"type":29,"value":409},{"type":24,"tag":96,"props":979,"children":980},{"style":130},[981],{"type":29,"value":339},{"type":24,"tag":96,"props":983,"children":984},{"style":174},[985],{"type":29,"value":418},{"type":24,"tag":96,"props":987,"children":988},{"style":130},[989],{"type":29,"value":143},{"type":24,"tag":96,"props":991,"children":992},{"style":113},[993],{"type":29,"value":427},{"type":24,"tag":96,"props":995,"children":996},{"style":113},[997],{"type":29,"value":432},{"type":24,"tag":96,"props":999,"children":1000},{"style":174},[1001],{"type":29,"value":437},{"type":24,"tag":96,"props":1003,"children":1004},{"style":130},[1005],{"type":29,"value":494},{"type":24,"tag":96,"props":1007,"children":1008},{"class":98,"line":201},[1009,1014,1018,1023,1027,1033],{"type":24,"tag":96,"props":1010,"children":1011},{"style":113},[1012],{"type":29,"value":1013},"        throw",{"type":24,"tag":96,"props":1015,"children":1016},{"style":130},[1017],{"type":29,"value":816},{"type":24,"tag":96,"props":1019,"children":1020},{"style":124},[1021],{"type":29,"value":1022},"InvalidOperationException",{"type":24,"tag":96,"props":1024,"children":1025},{"style":130},[1026],{"type":29,"value":339},{"type":24,"tag":96,"props":1028,"children":1030},{"style":1029},"--shiki-default:#98C379",[1031],{"type":29,"value":1032},"\"No active transaction\"",{"type":24,"tag":96,"props":1034,"children":1035},{"style":130},[1036],{"type":29,"value":650},{"type":24,"tag":96,"props":1038,"children":1039},{"class":98,"line":210},[1040],{"type":24,"tag":96,"props":1041,"children":1042},{"emptyLinePlaceholder":195},[1043],{"type":29,"value":198},{"type":24,"tag":96,"props":1045,"children":1046},{"class":98,"line":497},[1047],{"type":24,"tag":96,"props":1048,"children":1049},{"style":103},[1050],{"type":29,"value":1051},"    \u002F\u002F Copy so the caller can reuse their buffer\n",{"type":24,"tag":96,"props":1053,"children":1054},{"class":98,"line":515},[1055,1060,1065,1069,1073,1077,1081,1085,1089,1094],{"type":24,"tag":96,"props":1056,"children":1057},{"style":113},[1058],{"type":29,"value":1059},"    var",{"type":24,"tag":96,"props":1061,"children":1062},{"style":174},[1063],{"type":29,"value":1064}," copy",{"type":24,"tag":96,"props":1066,"children":1067},{"style":180},[1068],{"type":29,"value":183},{"type":24,"tag":96,"props":1070,"children":1071},{"style":130},[1072],{"type":29,"value":816},{"type":24,"tag":96,"props":1074,"children":1075},{"style":113},[1076],{"type":29,"value":166},{"type":24,"tag":96,"props":1078,"children":1079},{"style":130},[1080],{"type":29,"value":796},{"type":24,"tag":96,"props":1082,"children":1083},{"style":124},[1084],{"type":29,"value":926},{"type":24,"tag":96,"props":1086,"children":1087},{"style":130},[1088],{"type":29,"value":404},{"type":24,"tag":96,"props":1090,"children":1091},{"style":124},[1092],{"type":29,"value":1093},"Length",{"type":24,"tag":96,"props":1095,"children":1096},{"style":130},[1097],{"type":29,"value":1098},"];\n",{"type":24,"tag":96,"props":1100,"children":1101},{"class":98,"line":523},[1102,1107,1111,1116,1120,1125,1129,1135],{"type":24,"tag":96,"props":1103,"children":1104},{"style":124},[1105],{"type":29,"value":1106},"    data",{"type":24,"tag":96,"props":1108,"children":1109},{"style":130},[1110],{"type":29,"value":404},{"type":24,"tag":96,"props":1112,"children":1113},{"style":331},[1114],{"type":29,"value":1115},"CopyTo",{"type":24,"tag":96,"props":1117,"children":1118},{"style":130},[1119],{"type":29,"value":339},{"type":24,"tag":96,"props":1121,"children":1122},{"style":174},[1123],{"type":29,"value":1124},"copy",{"type":24,"tag":96,"props":1126,"children":1127},{"style":130},[1128],{"type":29,"value":143},{"type":24,"tag":96,"props":1130,"children":1132},{"style":1131},"--shiki-default:#D19A66",[1133],{"type":29,"value":1134},"0",{"type":24,"tag":96,"props":1136,"children":1137},{"style":130},[1138],{"type":29,"value":650},{"type":24,"tag":96,"props":1140,"children":1141},{"class":98,"line":532},[1142,1147,1151,1155,1159,1163,1167],{"type":24,"tag":96,"props":1143,"children":1144},{"style":124},[1145],{"type":29,"value":1146},"    txnCache",{"type":24,"tag":96,"props":1148,"children":1149},{"style":130},[1150],{"type":29,"value":796},{"type":24,"tag":96,"props":1152,"children":1153},{"style":174},[1154],{"type":29,"value":472},{"type":24,"tag":96,"props":1156,"children":1157},{"style":130},[1158],{"type":29,"value":806},{"type":24,"tag":96,"props":1160,"children":1161},{"style":180},[1162],{"type":29,"value":811},{"type":24,"tag":96,"props":1164,"children":1165},{"style":174},[1166],{"type":29,"value":1064},{"type":24,"tag":96,"props":1168,"children":1169},{"style":130},[1170],{"type":29,"value":512},{"type":24,"tag":96,"props":1172,"children":1173},{"class":98,"line":585},[1174],{"type":24,"tag":96,"props":1175,"children":1176},{"style":130},[1177],{"type":29,"value":659},{"type":24,"tag":25,"props":1179,"children":1180},{},[1181,1186,1188,1193,1195,1201],{"type":24,"tag":692,"props":1182,"children":1183},{},[1184],{"type":29,"value":1185},"Commit:",{"type":29,"value":1187}," Hand off the transaction's dirty pages to the group commit pipeline, which batches them into the WAL, flushes to disk, moves them into ",{"type":24,"tag":92,"props":1189,"children":1191},{"className":1190},[],[1192],{"type":29,"value":249},{"type":29,"value":1194},", and completes the ",{"type":24,"tag":92,"props":1196,"children":1198},{"className":1197},[],[1199],{"type":29,"value":1200},"TaskCompletionSource",{"type":29,"value":1202}," that the caller is awaiting.",{"type":24,"tag":85,"props":1204,"children":1206},{"className":87,"code":1205,"language":89,"meta":7,"style":7},"public async Task CommitTransactionAsync(ulong transactionId)\n{\n    if (!_walCache.TryRemove(transactionId, out var txnCache))\n        throw new InvalidOperationException(\"No active transaction\");\n\n    var pending = new PendingCommit(transactionId, txnCache, new TaskCompletionSource(\n        TaskCreationOptions.RunContinuationsAsynchronously));\n\n    await _commitChannel.Writer.WriteAsync(pending);\n    await pending.Completion.Task; \u002F\u002F Wait for group commit to finish\n}\n",[1207],{"type":24,"tag":92,"props":1208,"children":1209},{"__ignoreMap":7},[1210,1248,1255,1311,1338,1345,1401,1423,1430,1474,1513],{"type":24,"tag":96,"props":1211,"children":1212},{"class":98,"line":99},[1213,1217,1222,1227,1232,1236,1240,1244],{"type":24,"tag":96,"props":1214,"children":1215},{"style":113},[1216],{"type":29,"value":318},{"type":24,"tag":96,"props":1218,"children":1219},{"style":113},[1220],{"type":29,"value":1221}," async",{"type":24,"tag":96,"props":1223,"children":1224},{"style":124},[1225],{"type":29,"value":1226}," Task",{"type":24,"tag":96,"props":1228,"children":1229},{"style":331},[1230],{"type":29,"value":1231}," CommitTransactionAsync",{"type":24,"tag":96,"props":1233,"children":1234},{"style":130},[1235],{"type":29,"value":339},{"type":24,"tag":96,"props":1237,"children":1238},{"style":113},[1239],{"type":29,"value":138},{"type":24,"tag":96,"props":1241,"children":1242},{"style":124},[1243],{"type":29,"value":361},{"type":24,"tag":96,"props":1245,"children":1246},{"style":130},[1247],{"type":29,"value":366},{"type":24,"tag":96,"props":1249,"children":1250},{"class":98,"line":109},[1251],{"type":24,"tag":96,"props":1252,"children":1253},{"style":130},[1254],{"type":29,"value":374},{"type":24,"tag":96,"props":1256,"children":1257},{"class":98,"line":191},[1258,1262,1266,1270,1274,1278,1283,1287,1291,1295,1299,1303,1307],{"type":24,"tag":96,"props":1259,"children":1260},{"style":113},[1261],{"type":29,"value":390},{"type":24,"tag":96,"props":1263,"children":1264},{"style":130},[1265],{"type":29,"value":395},{"type":24,"tag":96,"props":1267,"children":1268},{"style":180},[1269],{"type":29,"value":965},{"type":24,"tag":96,"props":1271,"children":1272},{"style":124},[1273],{"type":29,"value":177},{"type":24,"tag":96,"props":1275,"children":1276},{"style":130},[1277],{"type":29,"value":404},{"type":24,"tag":96,"props":1279,"children":1280},{"style":331},[1281],{"type":29,"value":1282},"TryRemove",{"type":24,"tag":96,"props":1284,"children":1285},{"style":130},[1286],{"type":29,"value":339},{"type":24,"tag":96,"props":1288,"children":1289},{"style":174},[1290],{"type":29,"value":418},{"type":24,"tag":96,"props":1292,"children":1293},{"style":130},[1294],{"type":29,"value":143},{"type":24,"tag":96,"props":1296,"children":1297},{"style":113},[1298],{"type":29,"value":427},{"type":24,"tag":96,"props":1300,"children":1301},{"style":113},[1302],{"type":29,"value":432},{"type":24,"tag":96,"props":1304,"children":1305},{"style":174},[1306],{"type":29,"value":437},{"type":24,"tag":96,"props":1308,"children":1309},{"style":130},[1310],{"type":29,"value":494},{"type":24,"tag":96,"props":1312,"children":1313},{"class":98,"line":201},[1314,1318,1322,1326,1330,1334],{"type":24,"tag":96,"props":1315,"children":1316},{"style":113},[1317],{"type":29,"value":1013},{"type":24,"tag":96,"props":1319,"children":1320},{"style":130},[1321],{"type":29,"value":816},{"type":24,"tag":96,"props":1323,"children":1324},{"style":124},[1325],{"type":29,"value":1022},{"type":24,"tag":96,"props":1327,"children":1328},{"style":130},[1329],{"type":29,"value":339},{"type":24,"tag":96,"props":1331,"children":1332},{"style":1029},[1333],{"type":29,"value":1032},{"type":24,"tag":96,"props":1335,"children":1336},{"style":130},[1337],{"type":29,"value":650},{"type":24,"tag":96,"props":1339,"children":1340},{"class":98,"line":210},[1341],{"type":24,"tag":96,"props":1342,"children":1343},{"emptyLinePlaceholder":195},[1344],{"type":29,"value":198},{"type":24,"tag":96,"props":1346,"children":1347},{"class":98,"line":497},[1348,1352,1357,1361,1365,1370,1374,1378,1382,1387,1392,1396],{"type":24,"tag":96,"props":1349,"children":1350},{"style":113},[1351],{"type":29,"value":1059},{"type":24,"tag":96,"props":1353,"children":1354},{"style":174},[1355],{"type":29,"value":1356}," pending",{"type":24,"tag":96,"props":1358,"children":1359},{"style":180},[1360],{"type":29,"value":183},{"type":24,"tag":96,"props":1362,"children":1363},{"style":130},[1364],{"type":29,"value":816},{"type":24,"tag":96,"props":1366,"children":1367},{"style":124},[1368],{"type":29,"value":1369},"PendingCommit",{"type":24,"tag":96,"props":1371,"children":1372},{"style":130},[1373],{"type":29,"value":339},{"type":24,"tag":96,"props":1375,"children":1376},{"style":174},[1377],{"type":29,"value":418},{"type":24,"tag":96,"props":1379,"children":1380},{"style":130},[1381],{"type":29,"value":143},{"type":24,"tag":96,"props":1383,"children":1384},{"style":174},[1385],{"type":29,"value":1386},"txnCache",{"type":24,"tag":96,"props":1388,"children":1389},{"style":130},[1390],{"type":29,"value":1391},", new ",{"type":24,"tag":96,"props":1393,"children":1394},{"style":124},[1395],{"type":29,"value":1200},{"type":24,"tag":96,"props":1397,"children":1398},{"style":130},[1399],{"type":29,"value":1400},"(\n",{"type":24,"tag":96,"props":1402,"children":1403},{"class":98,"line":515},[1404,1409,1413,1418],{"type":24,"tag":96,"props":1405,"children":1406},{"style":124},[1407],{"type":29,"value":1408},"        TaskCreationOptions",{"type":24,"tag":96,"props":1410,"children":1411},{"style":130},[1412],{"type":29,"value":404},{"type":24,"tag":96,"props":1414,"children":1415},{"style":124},[1416],{"type":29,"value":1417},"RunContinuationsAsynchronously",{"type":24,"tag":96,"props":1419,"children":1420},{"style":130},[1421],{"type":29,"value":1422},"));\n",{"type":24,"tag":96,"props":1424,"children":1425},{"class":98,"line":523},[1426],{"type":24,"tag":96,"props":1427,"children":1428},{"emptyLinePlaceholder":195},[1429],{"type":29,"value":198},{"type":24,"tag":96,"props":1431,"children":1432},{"class":98,"line":532},[1433,1438,1443,1447,1452,1456,1461,1465,1470],{"type":24,"tag":96,"props":1434,"children":1435},{"style":130},[1436],{"type":29,"value":1437},"    await ",{"type":24,"tag":96,"props":1439,"children":1440},{"style":124},[1441],{"type":29,"value":1442},"_commitChannel",{"type":24,"tag":96,"props":1444,"children":1445},{"style":130},[1446],{"type":29,"value":404},{"type":24,"tag":96,"props":1448,"children":1449},{"style":124},[1450],{"type":29,"value":1451},"Writer",{"type":24,"tag":96,"props":1453,"children":1454},{"style":130},[1455],{"type":29,"value":404},{"type":24,"tag":96,"props":1457,"children":1458},{"style":331},[1459],{"type":29,"value":1460},"WriteAsync",{"type":24,"tag":96,"props":1462,"children":1463},{"style":130},[1464],{"type":29,"value":339},{"type":24,"tag":96,"props":1466,"children":1467},{"style":174},[1468],{"type":29,"value":1469},"pending",{"type":24,"tag":96,"props":1471,"children":1472},{"style":130},[1473],{"type":29,"value":650},{"type":24,"tag":96,"props":1475,"children":1476},{"class":98,"line":585},[1477,1481,1485,1489,1494,1498,1503,1508],{"type":24,"tag":96,"props":1478,"children":1479},{"style":130},[1480],{"type":29,"value":1437},{"type":24,"tag":96,"props":1482,"children":1483},{"style":124},[1484],{"type":29,"value":1469},{"type":24,"tag":96,"props":1486,"children":1487},{"style":130},[1488],{"type":29,"value":404},{"type":24,"tag":96,"props":1490,"children":1491},{"style":124},[1492],{"type":29,"value":1493},"Completion",{"type":24,"tag":96,"props":1495,"children":1496},{"style":130},[1497],{"type":29,"value":404},{"type":24,"tag":96,"props":1499,"children":1500},{"style":124},[1501],{"type":29,"value":1502},"Task",{"type":24,"tag":96,"props":1504,"children":1505},{"style":130},[1506],{"type":29,"value":1507},"; ",{"type":24,"tag":96,"props":1509,"children":1510},{"style":103},[1511],{"type":29,"value":1512},"\u002F\u002F Wait for group commit to finish\n",{"type":24,"tag":96,"props":1514,"children":1515},{"class":98,"line":601},[1516],{"type":24,"tag":96,"props":1517,"children":1518},{"style":130},[1519],{"type":29,"value":659},{"type":24,"tag":25,"props":1521,"children":1522},{},[1523,1528,1530,1535],{"type":24,"tag":692,"props":1524,"children":1525},{},[1526],{"type":29,"value":1527},"Abort:",{"type":29,"value":1529}," Just remove the transaction's entry from ",{"type":24,"tag":92,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":29,"value":177},{"type":29,"value":1536},". The dirty pages are gone. Zero I\u002FO.",{"type":24,"tag":85,"props":1538,"children":1540},{"className":87,"code":1539,"language":89,"meta":7,"style":7},"public void AbortTransaction(ulong transactionId)\n{\n    _walCache.TryRemove(transactionId, out _);\n}\n",[1541],{"type":24,"tag":92,"props":1542,"children":1543},{"__ignoreMap":7},[1544,1576,1583,1623],{"type":24,"tag":96,"props":1545,"children":1546},{"class":98,"line":99},[1547,1551,1555,1560,1564,1568,1572],{"type":24,"tag":96,"props":1548,"children":1549},{"style":113},[1550],{"type":29,"value":318},{"type":24,"tag":96,"props":1552,"children":1553},{"style":113},[1554],{"type":29,"value":892},{"type":24,"tag":96,"props":1556,"children":1557},{"style":331},[1558],{"type":29,"value":1559}," AbortTransaction",{"type":24,"tag":96,"props":1561,"children":1562},{"style":130},[1563],{"type":29,"value":339},{"type":24,"tag":96,"props":1565,"children":1566},{"style":113},[1567],{"type":29,"value":138},{"type":24,"tag":96,"props":1569,"children":1570},{"style":124},[1571],{"type":29,"value":361},{"type":24,"tag":96,"props":1573,"children":1574},{"style":130},[1575],{"type":29,"value":366},{"type":24,"tag":96,"props":1577,"children":1578},{"class":98,"line":109},[1579],{"type":24,"tag":96,"props":1580,"children":1581},{"style":130},[1582],{"type":29,"value":374},{"type":24,"tag":96,"props":1584,"children":1585},{"class":98,"line":191},[1586,1590,1594,1598,1602,1606,1610,1614,1619],{"type":24,"tag":96,"props":1587,"children":1588},{"style":124},[1589],{"type":29,"value":791},{"type":24,"tag":96,"props":1591,"children":1592},{"style":130},[1593],{"type":29,"value":404},{"type":24,"tag":96,"props":1595,"children":1596},{"style":331},[1597],{"type":29,"value":1282},{"type":24,"tag":96,"props":1599,"children":1600},{"style":130},[1601],{"type":29,"value":339},{"type":24,"tag":96,"props":1603,"children":1604},{"style":174},[1605],{"type":29,"value":418},{"type":24,"tag":96,"props":1607,"children":1608},{"style":130},[1609],{"type":29,"value":143},{"type":24,"tag":96,"props":1611,"children":1612},{"style":113},[1613],{"type":29,"value":427},{"type":24,"tag":96,"props":1615,"children":1616},{"style":174},[1617],{"type":29,"value":1618}," _",{"type":24,"tag":96,"props":1620,"children":1621},{"style":130},[1622],{"type":29,"value":650},{"type":24,"tag":96,"props":1624,"children":1625},{"class":98,"line":201},[1626],{"type":24,"tag":96,"props":1627,"children":1628},{"style":130},[1629],{"type":29,"value":659},{"type":24,"tag":25,"props":1631,"children":1632},{},[1633],{"type":29,"value":1634},"Rollback being free is a genuine advantage of the WAL cache design. There's no \"undo log\" to replay, no page reverts to write. The uncommitted data was never made visible to anyone else and simply ceases to exist.",{"type":24,"tag":37,"props":1636,"children":1637},{},[],{"type":24,"tag":41,"props":1639,"children":1641},{"id":1640},"group-commit-from-_walcache-to-_walindex",[1642,1644,1649,1651],{"type":29,"value":1643},"Group Commit: From ",{"type":24,"tag":92,"props":1645,"children":1647},{"className":1646},[],[1648],{"type":29,"value":177},{"type":29,"value":1650}," to ",{"type":24,"tag":92,"props":1652,"children":1654},{"className":1653},[],[1655],{"type":29,"value":249},{"type":24,"tag":25,"props":1657,"children":1658},{},[1659],{"type":29,"value":1660},"The group commit loop runs on a dedicated background task:",{"type":24,"tag":85,"props":1662,"children":1664},{"className":87,"code":1663,"language":89,"meta":7,"style":7},"private async Task GroupCommitLoopAsync(CancellationToken ct)\n{\n    await foreach (var pending in _commitChannel.Reader.ReadAllAsync(ct))\n    {\n        var batch = new List\u003CPendingCommit> { pending };\n\n        \u002F\u002F Drain any additional commits that arrived while we were processing\n        while (_commitChannel.Reader.TryRead(out var extra))\n            batch.Add(extra);\n\n        \u002F\u002F Write all batched pages to the WAL stream (single fsync)\n        await using var walLock = await _walLock.LockAsync();\n\n        foreach (var commit in batch)\n        {\n            WriteCommitToWalStream(commit);\n            \u002F\u002F Move committed pages to stable index\n            foreach (var (pageId, data) in commit.Pages)\n                _walIndex[pageId] = data;\n        }\n\n        await _walStream.FlushAsync();\n\n        \u002F\u002F Signal all waiters *outside* the lock\n        foreach (var commit in batch)\n            commit.Completion.TrySetResult();\n    }\n}\n",[1665],{"type":24,"tag":92,"props":1666,"children":1667},{"__ignoreMap":7},[1668,1706,1713,1779,1787,1835,1842,1850,1904,1934,1941,1949,1999,2006,2039,2048,2070,2079,2138,2172,2181,2189,2215,2223,2232,2264,2294,2303],{"type":24,"tag":96,"props":1669,"children":1670},{"class":98,"line":99},[1671,1675,1679,1683,1688,1692,1697,1702],{"type":24,"tag":96,"props":1672,"children":1673},{"style":113},[1674],{"type":29,"value":116},{"type":24,"tag":96,"props":1676,"children":1677},{"style":113},[1678],{"type":29,"value":1221},{"type":24,"tag":96,"props":1680,"children":1681},{"style":124},[1682],{"type":29,"value":1226},{"type":24,"tag":96,"props":1684,"children":1685},{"style":331},[1686],{"type":29,"value":1687}," GroupCommitLoopAsync",{"type":24,"tag":96,"props":1689,"children":1690},{"style":130},[1691],{"type":29,"value":339},{"type":24,"tag":96,"props":1693,"children":1694},{"style":124},[1695],{"type":29,"value":1696},"CancellationToken",{"type":24,"tag":96,"props":1698,"children":1699},{"style":124},[1700],{"type":29,"value":1701}," ct",{"type":24,"tag":96,"props":1703,"children":1704},{"style":130},[1705],{"type":29,"value":366},{"type":24,"tag":96,"props":1707,"children":1708},{"class":98,"line":109},[1709],{"type":24,"tag":96,"props":1710,"children":1711},{"style":130},[1712],{"type":29,"value":374},{"type":24,"tag":96,"props":1714,"children":1715},{"class":98,"line":191},[1716,1720,1725,1729,1734,1738,1743,1748,1752,1757,1761,1766,1770,1775],{"type":24,"tag":96,"props":1717,"children":1718},{"style":130},[1719],{"type":29,"value":1437},{"type":24,"tag":96,"props":1721,"children":1722},{"style":113},[1723],{"type":29,"value":1724},"foreach",{"type":24,"tag":96,"props":1726,"children":1727},{"style":130},[1728],{"type":29,"value":395},{"type":24,"tag":96,"props":1730,"children":1731},{"style":113},[1732],{"type":29,"value":1733},"var",{"type":24,"tag":96,"props":1735,"children":1736},{"style":174},[1737],{"type":29,"value":1356},{"type":24,"tag":96,"props":1739,"children":1740},{"style":113},[1741],{"type":29,"value":1742}," in",{"type":24,"tag":96,"props":1744,"children":1745},{"style":124},[1746],{"type":29,"value":1747}," _commitChannel",{"type":24,"tag":96,"props":1749,"children":1750},{"style":130},[1751],{"type":29,"value":404},{"type":24,"tag":96,"props":1753,"children":1754},{"style":124},[1755],{"type":29,"value":1756},"Reader",{"type":24,"tag":96,"props":1758,"children":1759},{"style":130},[1760],{"type":29,"value":404},{"type":24,"tag":96,"props":1762,"children":1763},{"style":331},[1764],{"type":29,"value":1765},"ReadAllAsync",{"type":24,"tag":96,"props":1767,"children":1768},{"style":130},[1769],{"type":29,"value":339},{"type":24,"tag":96,"props":1771,"children":1772},{"style":174},[1773],{"type":29,"value":1774},"ct",{"type":24,"tag":96,"props":1776,"children":1777},{"style":130},[1778],{"type":29,"value":494},{"type":24,"tag":96,"props":1780,"children":1781},{"class":98,"line":201},[1782],{"type":24,"tag":96,"props":1783,"children":1784},{"style":130},[1785],{"type":29,"value":1786},"    {\n",{"type":24,"tag":96,"props":1788,"children":1789},{"class":98,"line":210},[1790,1795,1800,1804,1808,1813,1817,1821,1826,1830],{"type":24,"tag":96,"props":1791,"children":1792},{"style":174},[1793],{"type":29,"value":1794},"        var",{"type":24,"tag":96,"props":1796,"children":1797},{"style":174},[1798],{"type":29,"value":1799}," batch",{"type":24,"tag":96,"props":1801,"children":1802},{"style":180},[1803],{"type":29,"value":183},{"type":24,"tag":96,"props":1805,"children":1806},{"style":130},[1807],{"type":29,"value":816},{"type":24,"tag":96,"props":1809,"children":1810},{"style":124},[1811],{"type":29,"value":1812},"List",{"type":24,"tag":96,"props":1814,"children":1815},{"style":130},[1816],{"type":29,"value":133},{"type":24,"tag":96,"props":1818,"children":1819},{"style":124},[1820],{"type":29,"value":1369},{"type":24,"tag":96,"props":1822,"children":1823},{"style":130},[1824],{"type":29,"value":1825},"> { ",{"type":24,"tag":96,"props":1827,"children":1828},{"style":174},[1829],{"type":29,"value":1469},{"type":24,"tag":96,"props":1831,"children":1832},{"style":130},[1833],{"type":29,"value":1834}," };\n",{"type":24,"tag":96,"props":1836,"children":1837},{"class":98,"line":497},[1838],{"type":24,"tag":96,"props":1839,"children":1840},{"emptyLinePlaceholder":195},[1841],{"type":29,"value":198},{"type":24,"tag":96,"props":1843,"children":1844},{"class":98,"line":515},[1845],{"type":24,"tag":96,"props":1846,"children":1847},{"style":103},[1848],{"type":29,"value":1849},"        \u002F\u002F Drain any additional commits that arrived while we were processing\n",{"type":24,"tag":96,"props":1851,"children":1852},{"class":98,"line":523},[1853,1858,1862,1866,1870,1874,1878,1883,1887,1891,1895,1900],{"type":24,"tag":96,"props":1854,"children":1855},{"style":331},[1856],{"type":29,"value":1857},"        while",{"type":24,"tag":96,"props":1859,"children":1860},{"style":130},[1861],{"type":29,"value":395},{"type":24,"tag":96,"props":1863,"children":1864},{"style":124},[1865],{"type":29,"value":1442},{"type":24,"tag":96,"props":1867,"children":1868},{"style":130},[1869],{"type":29,"value":404},{"type":24,"tag":96,"props":1871,"children":1872},{"style":124},[1873],{"type":29,"value":1756},{"type":24,"tag":96,"props":1875,"children":1876},{"style":130},[1877],{"type":29,"value":404},{"type":24,"tag":96,"props":1879,"children":1880},{"style":331},[1881],{"type":29,"value":1882},"TryRead",{"type":24,"tag":96,"props":1884,"children":1885},{"style":130},[1886],{"type":29,"value":339},{"type":24,"tag":96,"props":1888,"children":1889},{"style":113},[1890],{"type":29,"value":427},{"type":24,"tag":96,"props":1892,"children":1893},{"style":113},[1894],{"type":29,"value":432},{"type":24,"tag":96,"props":1896,"children":1897},{"style":174},[1898],{"type":29,"value":1899}," extra",{"type":24,"tag":96,"props":1901,"children":1902},{"style":130},[1903],{"type":29,"value":494},{"type":24,"tag":96,"props":1905,"children":1906},{"class":98,"line":532},[1907,1912,1916,1921,1925,1930],{"type":24,"tag":96,"props":1908,"children":1909},{"style":124},[1910],{"type":29,"value":1911},"            batch",{"type":24,"tag":96,"props":1913,"children":1914},{"style":130},[1915],{"type":29,"value":404},{"type":24,"tag":96,"props":1917,"children":1918},{"style":331},[1919],{"type":29,"value":1920},"Add",{"type":24,"tag":96,"props":1922,"children":1923},{"style":130},[1924],{"type":29,"value":339},{"type":24,"tag":96,"props":1926,"children":1927},{"style":174},[1928],{"type":29,"value":1929},"extra",{"type":24,"tag":96,"props":1931,"children":1932},{"style":130},[1933],{"type":29,"value":650},{"type":24,"tag":96,"props":1935,"children":1936},{"class":98,"line":585},[1937],{"type":24,"tag":96,"props":1938,"children":1939},{"emptyLinePlaceholder":195},[1940],{"type":29,"value":198},{"type":24,"tag":96,"props":1942,"children":1943},{"class":98,"line":601},[1944],{"type":24,"tag":96,"props":1945,"children":1946},{"style":103},[1947],{"type":29,"value":1948},"        \u002F\u002F Write all batched pages to the WAL stream (single fsync)\n",{"type":24,"tag":96,"props":1950,"children":1951},{"class":98,"line":609},[1952,1957,1962,1966,1971,1975,1980,1985,1989,1994],{"type":24,"tag":96,"props":1953,"children":1954},{"style":130},[1955],{"type":29,"value":1956},"        await ",{"type":24,"tag":96,"props":1958,"children":1959},{"style":174},[1960],{"type":29,"value":1961},"using",{"type":24,"tag":96,"props":1963,"children":1964},{"style":174},[1965],{"type":29,"value":432},{"type":24,"tag":96,"props":1967,"children":1968},{"style":174},[1969],{"type":29,"value":1970}," walLock",{"type":24,"tag":96,"props":1972,"children":1973},{"style":180},[1974],{"type":29,"value":183},{"type":24,"tag":96,"props":1976,"children":1977},{"style":130},[1978],{"type":29,"value":1979}," await ",{"type":24,"tag":96,"props":1981,"children":1982},{"style":124},[1983],{"type":29,"value":1984},"_walLock",{"type":24,"tag":96,"props":1986,"children":1987},{"style":130},[1988],{"type":29,"value":404},{"type":24,"tag":96,"props":1990,"children":1991},{"style":331},[1992],{"type":29,"value":1993},"LockAsync",{"type":24,"tag":96,"props":1995,"children":1996},{"style":130},[1997],{"type":29,"value":1998},"();\n",{"type":24,"tag":96,"props":2000,"children":2001},{"class":98,"line":618},[2002],{"type":24,"tag":96,"props":2003,"children":2004},{"emptyLinePlaceholder":195},[2005],{"type":29,"value":198},{"type":24,"tag":96,"props":2007,"children":2008},{"class":98,"line":653},[2009,2014,2018,2022,2027,2031,2035],{"type":24,"tag":96,"props":2010,"children":2011},{"style":331},[2012],{"type":29,"value":2013},"        foreach",{"type":24,"tag":96,"props":2015,"children":2016},{"style":130},[2017],{"type":29,"value":395},{"type":24,"tag":96,"props":2019,"children":2020},{"style":174},[2021],{"type":29,"value":1733},{"type":24,"tag":96,"props":2023,"children":2024},{"style":174},[2025],{"type":29,"value":2026}," commit",{"type":24,"tag":96,"props":2028,"children":2029},{"style":113},[2030],{"type":29,"value":1742},{"type":24,"tag":96,"props":2032,"children":2033},{"style":174},[2034],{"type":29,"value":1799},{"type":24,"tag":96,"props":2036,"children":2037},{"style":130},[2038],{"type":29,"value":366},{"type":24,"tag":96,"props":2040,"children":2042},{"class":98,"line":2041},15,[2043],{"type":24,"tag":96,"props":2044,"children":2045},{"style":130},[2046],{"type":29,"value":2047},"        {\n",{"type":24,"tag":96,"props":2049,"children":2051},{"class":98,"line":2050},16,[2052,2057,2061,2066],{"type":24,"tag":96,"props":2053,"children":2054},{"style":331},[2055],{"type":29,"value":2056},"            WriteCommitToWalStream",{"type":24,"tag":96,"props":2058,"children":2059},{"style":130},[2060],{"type":29,"value":339},{"type":24,"tag":96,"props":2062,"children":2063},{"style":174},[2064],{"type":29,"value":2065},"commit",{"type":24,"tag":96,"props":2067,"children":2068},{"style":130},[2069],{"type":29,"value":650},{"type":24,"tag":96,"props":2071,"children":2073},{"class":98,"line":2072},17,[2074],{"type":24,"tag":96,"props":2075,"children":2076},{"style":103},[2077],{"type":29,"value":2078},"            \u002F\u002F Move committed pages to stable index\n",{"type":24,"tag":96,"props":2080,"children":2082},{"class":98,"line":2081},18,[2083,2088,2092,2096,2100,2104,2108,2112,2116,2121,2125,2129,2134],{"type":24,"tag":96,"props":2084,"children":2085},{"style":331},[2086],{"type":29,"value":2087},"            foreach",{"type":24,"tag":96,"props":2089,"children":2090},{"style":130},[2091],{"type":29,"value":395},{"type":24,"tag":96,"props":2093,"children":2094},{"style":331},[2095],{"type":29,"value":1733},{"type":24,"tag":96,"props":2097,"children":2098},{"style":130},[2099],{"type":29,"value":395},{"type":24,"tag":96,"props":2101,"children":2102},{"style":174},[2103],{"type":29,"value":472},{"type":24,"tag":96,"props":2105,"children":2106},{"style":130},[2107],{"type":29,"value":143},{"type":24,"tag":96,"props":2109,"children":2110},{"style":174},[2111],{"type":29,"value":926},{"type":24,"tag":96,"props":2113,"children":2114},{"style":130},[2115],{"type":29,"value":442},{"type":24,"tag":96,"props":2117,"children":2118},{"style":113},[2119],{"type":29,"value":2120},"in",{"type":24,"tag":96,"props":2122,"children":2123},{"style":124},[2124],{"type":29,"value":2026},{"type":24,"tag":96,"props":2126,"children":2127},{"style":130},[2128],{"type":29,"value":404},{"type":24,"tag":96,"props":2130,"children":2131},{"style":124},[2132],{"type":29,"value":2133},"Pages",{"type":24,"tag":96,"props":2135,"children":2136},{"style":130},[2137],{"type":29,"value":366},{"type":24,"tag":96,"props":2139,"children":2141},{"class":98,"line":2140},19,[2142,2147,2151,2155,2159,2163,2168],{"type":24,"tag":96,"props":2143,"children":2144},{"style":124},[2145],{"type":29,"value":2146},"                _walIndex",{"type":24,"tag":96,"props":2148,"children":2149},{"style":130},[2150],{"type":29,"value":796},{"type":24,"tag":96,"props":2152,"children":2153},{"style":174},[2154],{"type":29,"value":472},{"type":24,"tag":96,"props":2156,"children":2157},{"style":130},[2158],{"type":29,"value":806},{"type":24,"tag":96,"props":2160,"children":2161},{"style":180},[2162],{"type":29,"value":811},{"type":24,"tag":96,"props":2164,"children":2165},{"style":174},[2166],{"type":29,"value":2167}," data",{"type":24,"tag":96,"props":2169,"children":2170},{"style":130},[2171],{"type":29,"value":512},{"type":24,"tag":96,"props":2173,"children":2175},{"class":98,"line":2174},20,[2176],{"type":24,"tag":96,"props":2177,"children":2178},{"style":130},[2179],{"type":29,"value":2180},"        }\n",{"type":24,"tag":96,"props":2182,"children":2184},{"class":98,"line":2183},21,[2185],{"type":24,"tag":96,"props":2186,"children":2187},{"emptyLinePlaceholder":195},[2188],{"type":29,"value":198},{"type":24,"tag":96,"props":2190,"children":2192},{"class":98,"line":2191},22,[2193,2197,2202,2206,2211],{"type":24,"tag":96,"props":2194,"children":2195},{"style":130},[2196],{"type":29,"value":1956},{"type":24,"tag":96,"props":2198,"children":2199},{"style":124},[2200],{"type":29,"value":2201},"_walStream",{"type":24,"tag":96,"props":2203,"children":2204},{"style":130},[2205],{"type":29,"value":404},{"type":24,"tag":96,"props":2207,"children":2208},{"style":331},[2209],{"type":29,"value":2210},"FlushAsync",{"type":24,"tag":96,"props":2212,"children":2213},{"style":130},[2214],{"type":29,"value":1998},{"type":24,"tag":96,"props":2216,"children":2218},{"class":98,"line":2217},23,[2219],{"type":24,"tag":96,"props":2220,"children":2221},{"emptyLinePlaceholder":195},[2222],{"type":29,"value":198},{"type":24,"tag":96,"props":2224,"children":2226},{"class":98,"line":2225},24,[2227],{"type":24,"tag":96,"props":2228,"children":2229},{"style":103},[2230],{"type":29,"value":2231},"        \u002F\u002F Signal all waiters *outside* the lock\n",{"type":24,"tag":96,"props":2233,"children":2235},{"class":98,"line":2234},25,[2236,2240,2244,2248,2252,2256,2260],{"type":24,"tag":96,"props":2237,"children":2238},{"style":331},[2239],{"type":29,"value":2013},{"type":24,"tag":96,"props":2241,"children":2242},{"style":130},[2243],{"type":29,"value":395},{"type":24,"tag":96,"props":2245,"children":2246},{"style":174},[2247],{"type":29,"value":1733},{"type":24,"tag":96,"props":2249,"children":2250},{"style":174},[2251],{"type":29,"value":2026},{"type":24,"tag":96,"props":2253,"children":2254},{"style":113},[2255],{"type":29,"value":1742},{"type":24,"tag":96,"props":2257,"children":2258},{"style":174},[2259],{"type":29,"value":1799},{"type":24,"tag":96,"props":2261,"children":2262},{"style":130},[2263],{"type":29,"value":366},{"type":24,"tag":96,"props":2265,"children":2267},{"class":98,"line":2266},26,[2268,2273,2277,2281,2285,2290],{"type":24,"tag":96,"props":2269,"children":2270},{"style":124},[2271],{"type":29,"value":2272},"            commit",{"type":24,"tag":96,"props":2274,"children":2275},{"style":130},[2276],{"type":29,"value":404},{"type":24,"tag":96,"props":2278,"children":2279},{"style":124},[2280],{"type":29,"value":1493},{"type":24,"tag":96,"props":2282,"children":2283},{"style":130},[2284],{"type":29,"value":404},{"type":24,"tag":96,"props":2286,"children":2287},{"style":331},[2288],{"type":29,"value":2289},"TrySetResult",{"type":24,"tag":96,"props":2291,"children":2292},{"style":130},[2293],{"type":29,"value":1998},{"type":24,"tag":96,"props":2295,"children":2297},{"class":98,"line":2296},27,[2298],{"type":24,"tag":96,"props":2299,"children":2300},{"style":130},[2301],{"type":29,"value":2302},"    }\n",{"type":24,"tag":96,"props":2304,"children":2306},{"class":98,"line":2305},28,[2307],{"type":24,"tag":96,"props":2308,"children":2309},{"style":130},[2310],{"type":29,"value":659},{"type":24,"tag":25,"props":2312,"children":2313},{},[2314,2316,2321],{"type":29,"value":2315},"All transactions in the batch share a single ",{"type":24,"tag":92,"props":2317,"children":2319},{"className":2318},[],[2320],{"type":29,"value":2210},{"type":29,"value":2322}," call. This is the group commit dividend: instead of one fsync per commit (a ~10ms disk operation), N transactions share one fsync at ~10ms total. At high commit rates, this is the difference between 100 commits\u002Fsec and 10,000 commits\u002Fsec.",{"type":24,"tag":37,"props":2324,"children":2325},{},[],{"type":24,"tag":41,"props":2327,"children":2329},{"id":2328},"why-semaphoreslim-on-the-wal-stream",[2330,2332,2338],{"type":29,"value":2331},"Why ",{"type":24,"tag":92,"props":2333,"children":2335},{"className":2334},[],[2336],{"type":29,"value":2337},"SemaphoreSlim",{"type":29,"value":2339}," on the WAL Stream",{"type":24,"tag":25,"props":2341,"children":2342},{},[2343,2345,2350],{"type":29,"value":2344},"The WAL stream is shared state. Any concurrent commit loop iteration would corrupt it. BLite uses a ",{"type":24,"tag":92,"props":2346,"children":2348},{"className":2347},[],[2349],{"type":29,"value":2337},{"type":29,"value":2351},"-backed async lock:",{"type":24,"tag":85,"props":2353,"children":2355},{"className":87,"code":2354,"language":89,"meta":7,"style":7},"private readonly SemaphoreSlim _walLock = new(1, 1);\n\n\u002F\u002F Convenience wrapper that disposes the semaphore on exit\nprivate async Task\u003CIDisposable> LockAsync()\n{\n    await _walLock.WaitAsync();\n    return new SemaphoreReleaser(_walLock);\n}\n",[2356],{"type":24,"tag":92,"props":2357,"children":2358},{"__ignoreMap":7},[2359,2406,2413,2421,2458,2465,2489,2517],{"type":24,"tag":96,"props":2360,"children":2361},{"class":98,"line":99},[2362,2366,2370,2375,2380,2384,2389,2394,2398,2402],{"type":24,"tag":96,"props":2363,"children":2364},{"style":113},[2365],{"type":29,"value":116},{"type":24,"tag":96,"props":2367,"children":2368},{"style":113},[2369],{"type":29,"value":121},{"type":24,"tag":96,"props":2371,"children":2372},{"style":124},[2373],{"type":29,"value":2374}," SemaphoreSlim",{"type":24,"tag":96,"props":2376,"children":2377},{"style":174},[2378],{"type":29,"value":2379}," _walLock",{"type":24,"tag":96,"props":2381,"children":2382},{"style":180},[2383],{"type":29,"value":183},{"type":24,"tag":96,"props":2385,"children":2386},{"style":130},[2387],{"type":29,"value":2388}," new(",{"type":24,"tag":96,"props":2390,"children":2391},{"style":1131},[2392],{"type":29,"value":2393},"1",{"type":24,"tag":96,"props":2395,"children":2396},{"style":130},[2397],{"type":29,"value":143},{"type":24,"tag":96,"props":2399,"children":2400},{"style":1131},[2401],{"type":29,"value":2393},{"type":24,"tag":96,"props":2403,"children":2404},{"style":130},[2405],{"type":29,"value":650},{"type":24,"tag":96,"props":2407,"children":2408},{"class":98,"line":109},[2409],{"type":24,"tag":96,"props":2410,"children":2411},{"emptyLinePlaceholder":195},[2412],{"type":29,"value":198},{"type":24,"tag":96,"props":2414,"children":2415},{"class":98,"line":191},[2416],{"type":24,"tag":96,"props":2417,"children":2418},{"style":103},[2419],{"type":29,"value":2420},"\u002F\u002F Convenience wrapper that disposes the semaphore on exit\n",{"type":24,"tag":96,"props":2422,"children":2423},{"class":98,"line":201},[2424,2428,2432,2436,2440,2445,2450,2454],{"type":24,"tag":96,"props":2425,"children":2426},{"style":113},[2427],{"type":29,"value":116},{"type":24,"tag":96,"props":2429,"children":2430},{"style":113},[2431],{"type":29,"value":1221},{"type":24,"tag":96,"props":2433,"children":2434},{"style":124},[2435],{"type":29,"value":1226},{"type":24,"tag":96,"props":2437,"children":2438},{"style":130},[2439],{"type":29,"value":133},{"type":24,"tag":96,"props":2441,"children":2442},{"style":124},[2443],{"type":29,"value":2444},"IDisposable",{"type":24,"tag":96,"props":2446,"children":2447},{"style":130},[2448],{"type":29,"value":2449},"> ",{"type":24,"tag":96,"props":2451,"children":2452},{"style":331},[2453],{"type":29,"value":1993},{"type":24,"tag":96,"props":2455,"children":2456},{"style":130},[2457],{"type":29,"value":727},{"type":24,"tag":96,"props":2459,"children":2460},{"class":98,"line":210},[2461],{"type":24,"tag":96,"props":2462,"children":2463},{"style":130},[2464],{"type":29,"value":374},{"type":24,"tag":96,"props":2466,"children":2467},{"class":98,"line":497},[2468,2472,2476,2480,2485],{"type":24,"tag":96,"props":2469,"children":2470},{"style":130},[2471],{"type":29,"value":1437},{"type":24,"tag":96,"props":2473,"children":2474},{"style":124},[2475],{"type":29,"value":1984},{"type":24,"tag":96,"props":2477,"children":2478},{"style":130},[2479],{"type":29,"value":404},{"type":24,"tag":96,"props":2481,"children":2482},{"style":331},[2483],{"type":29,"value":2484},"WaitAsync",{"type":24,"tag":96,"props":2486,"children":2487},{"style":130},[2488],{"type":29,"value":1998},{"type":24,"tag":96,"props":2490,"children":2491},{"class":98,"line":515},[2492,2496,2500,2505,2509,2513],{"type":24,"tag":96,"props":2493,"children":2494},{"style":113},[2495],{"type":29,"value":624},{"type":24,"tag":96,"props":2497,"children":2498},{"style":130},[2499],{"type":29,"value":816},{"type":24,"tag":96,"props":2501,"children":2502},{"style":124},[2503],{"type":29,"value":2504},"SemaphoreReleaser",{"type":24,"tag":96,"props":2506,"children":2507},{"style":130},[2508],{"type":29,"value":339},{"type":24,"tag":96,"props":2510,"children":2511},{"style":174},[2512],{"type":29,"value":1984},{"type":24,"tag":96,"props":2514,"children":2515},{"style":130},[2516],{"type":29,"value":650},{"type":24,"tag":96,"props":2518,"children":2519},{"class":98,"line":523},[2520],{"type":24,"tag":96,"props":2521,"children":2522},{"style":130},[2523],{"type":29,"value":659},{"type":24,"tag":25,"props":2525,"children":2526},{},[2527,2532,2534,2540,2542,2547],{"type":24,"tag":92,"props":2528,"children":2530},{"className":2529},[],[2531],{"type":29,"value":2337},{"type":29,"value":2533}," with initial count 1 is the idiomatic async mutex in .NET. Unlike ",{"type":24,"tag":92,"props":2535,"children":2537},{"className":2536},[],[2538],{"type":29,"value":2539},"lock",{"type":29,"value":2541},", it doesn't block a thread while waiting — it yields back to the thread pool, which matters when the lock is held during ",{"type":24,"tag":92,"props":2543,"children":2545},{"className":2544},[],[2546],{"type":29,"value":2210},{"type":29,"value":2548}," (an async operation that would deadlock under a synchronous lock).",{"type":24,"tag":37,"props":2550,"children":2551},{},[],{"type":24,"tag":41,"props":2553,"children":2555},{"id":2554},"checkpoint-collapsing-wal-into-the-page-file",[2556],{"type":29,"value":2557},"Checkpoint: Collapsing WAL into the Page File",{"type":24,"tag":25,"props":2559,"children":2560},{},[2561,2563,2568,2570,2575],{"type":29,"value":2562},"As transactions commit, ",{"type":24,"tag":92,"props":2564,"children":2566},{"className":2565},[],[2567],{"type":29,"value":249},{"type":29,"value":2569}," grows. Eventually it's large enough that startup time (reapplying the WAL) would be unacceptable during recovery. A checkpoint merges the ",{"type":24,"tag":92,"props":2571,"children":2573},{"className":2572},[],[2574],{"type":29,"value":249},{"type":29,"value":2576}," into the durable page file and truncates the WAL:",{"type":24,"tag":85,"props":2578,"children":2580},{"className":87,"code":2579,"language":89,"meta":7,"style":7},"private async Task CheckpointInternalAsync(CancellationToken ct)\n{\n    await using var walLock = await _walLock.LockAsync();\n\n    \u002F\u002F Write all stable pages to the page file\n    foreach (var (pageId, data) in _walIndex)\n        await _pageFile.WritePageAsync(pageId, data, ct);\n\n    await _pageFile.FlushAsync(ct);\n\n    \u002F\u002F Now it's safe to truncate the WAL\n    _walStream.SetLength(0);\n    _walStream.Seek(0, SeekOrigin.Begin);\n    _walIndex.Clear();\n}\n",[2581],{"type":24,"tag":92,"props":2582,"children":2583},{"__ignoreMap":7},[2584,2620,2627,2670,2677,2685,2719,2768,2775,2806,2813,2821,2850,2896,2917],{"type":24,"tag":96,"props":2585,"children":2586},{"class":98,"line":99},[2587,2591,2595,2599,2604,2608,2612,2616],{"type":24,"tag":96,"props":2588,"children":2589},{"style":113},[2590],{"type":29,"value":116},{"type":24,"tag":96,"props":2592,"children":2593},{"style":113},[2594],{"type":29,"value":1221},{"type":24,"tag":96,"props":2596,"children":2597},{"style":124},[2598],{"type":29,"value":1226},{"type":24,"tag":96,"props":2600,"children":2601},{"style":331},[2602],{"type":29,"value":2603}," CheckpointInternalAsync",{"type":24,"tag":96,"props":2605,"children":2606},{"style":130},[2607],{"type":29,"value":339},{"type":24,"tag":96,"props":2609,"children":2610},{"style":124},[2611],{"type":29,"value":1696},{"type":24,"tag":96,"props":2613,"children":2614},{"style":124},[2615],{"type":29,"value":1701},{"type":24,"tag":96,"props":2617,"children":2618},{"style":130},[2619],{"type":29,"value":366},{"type":24,"tag":96,"props":2621,"children":2622},{"class":98,"line":109},[2623],{"type":24,"tag":96,"props":2624,"children":2625},{"style":130},[2626],{"type":29,"value":374},{"type":24,"tag":96,"props":2628,"children":2629},{"class":98,"line":191},[2630,2634,2638,2642,2646,2650,2654,2658,2662,2666],{"type":24,"tag":96,"props":2631,"children":2632},{"style":130},[2633],{"type":29,"value":1437},{"type":24,"tag":96,"props":2635,"children":2636},{"style":113},[2637],{"type":29,"value":1961},{"type":24,"tag":96,"props":2639,"children":2640},{"style":113},[2641],{"type":29,"value":432},{"type":24,"tag":96,"props":2643,"children":2644},{"style":174},[2645],{"type":29,"value":1970},{"type":24,"tag":96,"props":2647,"children":2648},{"style":180},[2649],{"type":29,"value":183},{"type":24,"tag":96,"props":2651,"children":2652},{"style":130},[2653],{"type":29,"value":1979},{"type":24,"tag":96,"props":2655,"children":2656},{"style":124},[2657],{"type":29,"value":1984},{"type":24,"tag":96,"props":2659,"children":2660},{"style":130},[2661],{"type":29,"value":404},{"type":24,"tag":96,"props":2663,"children":2664},{"style":331},[2665],{"type":29,"value":1993},{"type":24,"tag":96,"props":2667,"children":2668},{"style":130},[2669],{"type":29,"value":1998},{"type":24,"tag":96,"props":2671,"children":2672},{"class":98,"line":201},[2673],{"type":24,"tag":96,"props":2674,"children":2675},{"emptyLinePlaceholder":195},[2676],{"type":29,"value":198},{"type":24,"tag":96,"props":2678,"children":2679},{"class":98,"line":210},[2680],{"type":24,"tag":96,"props":2681,"children":2682},{"style":103},[2683],{"type":29,"value":2684},"    \u002F\u002F Write all stable pages to the page file\n",{"type":24,"tag":96,"props":2686,"children":2687},{"class":98,"line":497},[2688,2693,2697,2701,2706,2710,2715],{"type":24,"tag":96,"props":2689,"children":2690},{"style":113},[2691],{"type":29,"value":2692},"    foreach",{"type":24,"tag":96,"props":2694,"children":2695},{"style":130},[2696],{"type":29,"value":395},{"type":24,"tag":96,"props":2698,"children":2699},{"style":113},[2700],{"type":29,"value":1733},{"type":24,"tag":96,"props":2702,"children":2703},{"style":130},[2704],{"type":29,"value":2705}," (pageId, data) ",{"type":24,"tag":96,"props":2707,"children":2708},{"style":113},[2709],{"type":29,"value":2120},{"type":24,"tag":96,"props":2711,"children":2712},{"style":174},[2713],{"type":29,"value":2714}," _walIndex",{"type":24,"tag":96,"props":2716,"children":2717},{"style":130},[2718],{"type":29,"value":366},{"type":24,"tag":96,"props":2720,"children":2721},{"class":98,"line":515},[2722,2726,2731,2735,2740,2744,2748,2752,2756,2760,2764],{"type":24,"tag":96,"props":2723,"children":2724},{"style":130},[2725],{"type":29,"value":1956},{"type":24,"tag":96,"props":2727,"children":2728},{"style":124},[2729],{"type":29,"value":2730},"_pageFile",{"type":24,"tag":96,"props":2732,"children":2733},{"style":130},[2734],{"type":29,"value":404},{"type":24,"tag":96,"props":2736,"children":2737},{"style":331},[2738],{"type":29,"value":2739},"WritePageAsync",{"type":24,"tag":96,"props":2741,"children":2742},{"style":130},[2743],{"type":29,"value":339},{"type":24,"tag":96,"props":2745,"children":2746},{"style":174},[2747],{"type":29,"value":472},{"type":24,"tag":96,"props":2749,"children":2750},{"style":130},[2751],{"type":29,"value":143},{"type":24,"tag":96,"props":2753,"children":2754},{"style":174},[2755],{"type":29,"value":926},{"type":24,"tag":96,"props":2757,"children":2758},{"style":130},[2759],{"type":29,"value":143},{"type":24,"tag":96,"props":2761,"children":2762},{"style":174},[2763],{"type":29,"value":1774},{"type":24,"tag":96,"props":2765,"children":2766},{"style":130},[2767],{"type":29,"value":650},{"type":24,"tag":96,"props":2769,"children":2770},{"class":98,"line":523},[2771],{"type":24,"tag":96,"props":2772,"children":2773},{"emptyLinePlaceholder":195},[2774],{"type":29,"value":198},{"type":24,"tag":96,"props":2776,"children":2777},{"class":98,"line":532},[2778,2782,2786,2790,2794,2798,2802],{"type":24,"tag":96,"props":2779,"children":2780},{"style":130},[2781],{"type":29,"value":1437},{"type":24,"tag":96,"props":2783,"children":2784},{"style":124},[2785],{"type":29,"value":2730},{"type":24,"tag":96,"props":2787,"children":2788},{"style":130},[2789],{"type":29,"value":404},{"type":24,"tag":96,"props":2791,"children":2792},{"style":331},[2793],{"type":29,"value":2210},{"type":24,"tag":96,"props":2795,"children":2796},{"style":130},[2797],{"type":29,"value":339},{"type":24,"tag":96,"props":2799,"children":2800},{"style":174},[2801],{"type":29,"value":1774},{"type":24,"tag":96,"props":2803,"children":2804},{"style":130},[2805],{"type":29,"value":650},{"type":24,"tag":96,"props":2807,"children":2808},{"class":98,"line":585},[2809],{"type":24,"tag":96,"props":2810,"children":2811},{"emptyLinePlaceholder":195},[2812],{"type":29,"value":198},{"type":24,"tag":96,"props":2814,"children":2815},{"class":98,"line":601},[2816],{"type":24,"tag":96,"props":2817,"children":2818},{"style":103},[2819],{"type":29,"value":2820},"    \u002F\u002F Now it's safe to truncate the WAL\n",{"type":24,"tag":96,"props":2822,"children":2823},{"class":98,"line":609},[2824,2829,2833,2838,2842,2846],{"type":24,"tag":96,"props":2825,"children":2826},{"style":124},[2827],{"type":29,"value":2828},"    _walStream",{"type":24,"tag":96,"props":2830,"children":2831},{"style":130},[2832],{"type":29,"value":404},{"type":24,"tag":96,"props":2834,"children":2835},{"style":331},[2836],{"type":29,"value":2837},"SetLength",{"type":24,"tag":96,"props":2839,"children":2840},{"style":130},[2841],{"type":29,"value":339},{"type":24,"tag":96,"props":2843,"children":2844},{"style":1131},[2845],{"type":29,"value":1134},{"type":24,"tag":96,"props":2847,"children":2848},{"style":130},[2849],{"type":29,"value":650},{"type":24,"tag":96,"props":2851,"children":2852},{"class":98,"line":618},[2853,2857,2861,2866,2870,2874,2878,2883,2887,2892],{"type":24,"tag":96,"props":2854,"children":2855},{"style":124},[2856],{"type":29,"value":2828},{"type":24,"tag":96,"props":2858,"children":2859},{"style":130},[2860],{"type":29,"value":404},{"type":24,"tag":96,"props":2862,"children":2863},{"style":331},[2864],{"type":29,"value":2865},"Seek",{"type":24,"tag":96,"props":2867,"children":2868},{"style":130},[2869],{"type":29,"value":339},{"type":24,"tag":96,"props":2871,"children":2872},{"style":1131},[2873],{"type":29,"value":1134},{"type":24,"tag":96,"props":2875,"children":2876},{"style":130},[2877],{"type":29,"value":143},{"type":24,"tag":96,"props":2879,"children":2880},{"style":124},[2881],{"type":29,"value":2882},"SeekOrigin",{"type":24,"tag":96,"props":2884,"children":2885},{"style":130},[2886],{"type":29,"value":404},{"type":24,"tag":96,"props":2888,"children":2889},{"style":124},[2890],{"type":29,"value":2891},"Begin",{"type":24,"tag":96,"props":2893,"children":2894},{"style":130},[2895],{"type":29,"value":650},{"type":24,"tag":96,"props":2897,"children":2898},{"class":98,"line":653},[2899,2904,2908,2913],{"type":24,"tag":96,"props":2900,"children":2901},{"style":124},[2902],{"type":29,"value":2903},"    _walIndex",{"type":24,"tag":96,"props":2905,"children":2906},{"style":130},[2907],{"type":29,"value":404},{"type":24,"tag":96,"props":2909,"children":2910},{"style":331},[2911],{"type":29,"value":2912},"Clear",{"type":24,"tag":96,"props":2914,"children":2915},{"style":130},[2916],{"type":29,"value":1998},{"type":24,"tag":96,"props":2918,"children":2919},{"class":98,"line":2041},[2920],{"type":24,"tag":96,"props":2921,"children":2922},{"style":130},[2923],{"type":29,"value":659},{"type":24,"tag":25,"props":2925,"children":2926},{},[2927,2929,2934,2936,2941],{"type":29,"value":2928},"Checkpointing is serialized under ",{"type":24,"tag":92,"props":2930,"children":2932},{"className":2931},[],[2933],{"type":29,"value":1984},{"type":29,"value":2935},", which means it blocks the group commit loop. During a checkpoint, incoming commit calls queue up in ",{"type":24,"tag":92,"props":2937,"children":2939},{"className":2938},[],[2940],{"type":29,"value":1442},{"type":29,"value":2942}," and resume once the lock is released. The lock duration is proportional to the number of dirty pages — another argument for frequent small checkpoints over rare large ones.",{"type":24,"tag":37,"props":2944,"children":2945},{},[],{"type":24,"tag":41,"props":2947,"children":2949},{"id":2948},"where-this-diverges-from-true-mvcc",[2950],{"type":29,"value":2951},"Where This Diverges from True MVCC",{"type":24,"tag":25,"props":2953,"children":2954},{},[2955],{"type":29,"value":2956},"BLite's model gives you the ACID properties you care about for an embedded database:",{"type":24,"tag":2958,"props":2959,"children":2960},"ul",{},[2961,2972,2982,2992],{"type":24,"tag":2962,"props":2963,"children":2964},"li",{},[2965,2970],{"type":24,"tag":692,"props":2966,"children":2967},{},[2968],{"type":29,"value":2969},"Atomicity",{"type":29,"value":2971},": uncommitted pages are invisible; rollback is free.",{"type":24,"tag":2962,"props":2973,"children":2974},{},[2975,2980],{"type":24,"tag":692,"props":2976,"children":2977},{},[2978],{"type":29,"value":2979},"Consistency",{"type":29,"value":2981},": constraints are enforced at the document layer before pages are written.",{"type":24,"tag":2962,"props":2983,"children":2984},{},[2985,2990],{"type":24,"tag":692,"props":2986,"children":2987},{},[2988],{"type":29,"value":2989},"Isolation",{"type":29,"value":2991},": each transaction reads a consistent view — its own writes layered on top of the last committed state.",{"type":24,"tag":2962,"props":2993,"children":2994},{},[2995,3000,3002,3007],{"type":24,"tag":692,"props":2996,"children":2997},{},[2998],{"type":29,"value":2999},"Durability",{"type":29,"value":3001},": committed transactions survive a crash because they're in the WAL before the ",{"type":24,"tag":92,"props":3003,"children":3005},{"className":3004},[],[3006],{"type":29,"value":2289},{"type":29,"value":3008}," fires.",{"type":24,"tag":25,"props":3010,"children":3011},{},[3012],{"type":29,"value":3013},"But it's not full MVCC in the database-theory sense:",{"type":24,"tag":25,"props":3015,"children":3016},{},[3017,3022,3024,3029],{"type":24,"tag":692,"props":3018,"children":3019},{},[3020],{"type":29,"value":3021},"No snapshot isolation.",{"type":29,"value":3023}," Two concurrent readers do not each see the state of the database at their individual start times. They both read from the same ",{"type":24,"tag":92,"props":3025,"children":3027},{"className":3026},[],[3028],{"type":29,"value":249},{"type":29,"value":3030}," snapshot — the state as of the last committed transaction. If transaction A reads page 5, then transaction B commits an update to page 5, then transaction A reads page 5 again, it sees B's committed update. This is \"read committed\" isolation, not \"repeatable read\" or \"serializable.\"",{"type":24,"tag":25,"props":3032,"children":3033},{},[3034,3039,3041,3046,3048,3053],{"type":24,"tag":692,"props":3035,"children":3036},{},[3037],{"type":29,"value":3038},"Single logical writer.",{"type":29,"value":3040}," Commits are serialized through ",{"type":24,"tag":92,"props":3042,"children":3044},{"className":3043},[],[3045],{"type":29,"value":1442},{"type":29,"value":3047},". Concurrent transactions can prepare their dirty pages in parallel (each in its own ",{"type":24,"tag":92,"props":3049,"children":3051},{"className":3050},[],[3052],{"type":29,"value":177},{"type":29,"value":3054}," entry) but only one commit makes it through the group commit loop at a time. There's no concurrent write path — the \"group\" in group commit means batching, not parallelism.",{"type":24,"tag":25,"props":3056,"children":3057},{},[3058,3063,3065,3070],{"type":24,"tag":692,"props":3059,"children":3060},{},[3061],{"type":29,"value":3062},"Readers can stall during checkpoint.",{"type":29,"value":3064}," The checkpoint loop holds ",{"type":24,"tag":92,"props":3066,"children":3068},{"className":3067},[],[3069],{"type":29,"value":1984},{"type":29,"value":3071},", which serializes against the commit loop, which serializes against all new commits. A long checkpoint can delay all new commits for the duration. A proper MVCC system would let readers proceed against old versions while the checkpoint writes new ones.",{"type":24,"tag":37,"props":3073,"children":3074},{},[],{"type":24,"tag":41,"props":3076,"children":3078},{"id":3077},"the-bottom-line",[3079],{"type":29,"value":3080},"The Bottom Line",{"type":24,"tag":25,"props":3082,"children":3083},{},[3084],{"type":29,"value":3085},"For an embedded, single-writer document database, BLite's two-dictionary design is a pragmatic sweet spot. Rollback is a dictionary remove. Reads always see a consistent committed state. Group commit amortizes disk latency across concurrent transactions. And the implementation is small enough to read in an afternoon.",{"type":24,"tag":25,"props":3087,"children":3088},{},[3089],{"type":29,"value":3090},"The limitations — read committed isolation, serialized writers, blocking checkpoint — are real. But for the overwhelmingly common case of \"one process, sequential or lightly concurrent access,\" they don't matter in practice.",{"type":24,"tag":25,"props":3092,"children":3093},{},[3094,3096,3105],{"type":29,"value":3095},"The complete source is on ",{"type":24,"tag":3097,"props":3098,"children":3102},"a",{"href":3099,"rel":3100},"https:\u002F\u002Fgithub.com\u002FEntglDb\u002FBLite",[3101],"nofollow",[3103],{"type":29,"value":3104},"GitHub",{"type":29,"value":404},{"type":24,"tag":3107,"props":3108,"children":3109},"style",{},[3110],{"type":29,"value":3111},"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);}",{"title":7,"searchDepth":109,"depth":109,"links":3113},[3114,3115,3116,3117,3119,3121,3122,3123],{"id":43,"depth":109,"text":46},{"id":75,"depth":109,"text":78},{"id":684,"depth":109,"text":687},{"id":1640,"depth":109,"text":3118},"Group Commit: From _walCache to _walIndex",{"id":2328,"depth":109,"text":3120},"Why SemaphoreSlim on the WAL Stream",{"id":2554,"depth":109,"text":2557},{"id":2948,"depth":109,"text":2951},{"id":3077,"depth":109,"text":3080},"markdown","content:en:blog:mvcc-transactions-in-blite.md","content","en\u002Fblog\u002Fmvcc-transactions-in-blite.md","en\u002Fblog\u002Fmvcc-transactions-in-blite","md",{"loc":4},1775332618206]