From 4ec63d4bd8aeef14af56e3103a9f7020c6b7bf7c Mon Sep 17 00:00:00 2001 From: beo3000 Date: Mon, 2 Mar 2026 21:28:53 +0100 Subject: [PATCH] deploy fix --- ka-note/VERSION | 2 +- ka-note/deploy.ps1 | 21 +++++++++++++++++++-- ka-note/server/ka-note.db-shm | Bin 32768 -> 32768 bytes ka-note/server/ka-note.db-wal | Bin 4264232 -> 4264232 bytes ka-note/server/src/routes/admin.ts | 23 ++++++++++++++++++++--- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/ka-note/VERSION b/ka-note/VERSION index 6581d43..911e8ab 100644 --- a/ka-note/VERSION +++ b/ka-note/VERSION @@ -1 +1 @@ -1.2.17 \ No newline at end of file +1.2.18 \ No newline at end of file diff --git a/ka-note/deploy.ps1 b/ka-note/deploy.ps1 index 92b96a6..2796467 100644 --- a/ka-note/deploy.ps1 +++ b/ka-note/deploy.ps1 @@ -42,6 +42,22 @@ $BackupDir = Join-Path $PSScriptRoot "backups" $null = New-Item -ItemType Directory -Path $BackupDir -Force $BackupFile = Join-Path $BackupDir ("ka-note-pre-deploy-$VERSION.db") +# Flush WAL before download so the snapshot is consistent (WAL file not included in Kudu download). +$AppUrl = "https://$APP.azurewebsites.net" +if ($env:KA_NOTE_DEPLOY_API_KEY) { + Write-Host " Flushing WAL checkpoint before download..." -ForegroundColor DarkGray + try { + $cpResult = Invoke-RestMethod -Uri "$AppUrl/api/admin/checkpoint" -Method POST ` + -Headers @{ Authorization = "Bearer $env:KA_NOTE_DEPLOY_API_KEY" } ` + -UseBasicParsing -TimeoutSec 30 + Write-Host " Checkpoint: $($cpResult.checkpointed)/$($cpResult.log) pages" -ForegroundColor DarkGray + } catch { + Write-Host " Checkpoint call failed (continuing anyway): $_" -ForegroundColor Yellow + } +} else { + Write-Host " KA_NOTE_DEPLOY_API_KEY not set, skipping WAL checkpoint" -ForegroundColor Yellow +} + Write-Host " Downloading ka-note.db from prod..." -ForegroundColor DarkGray try { Invoke-WebRequest -Uri $KuduDbUrl ` @@ -107,7 +123,6 @@ az webapp config appsettings set --name $APP --resource-group $RG --settings ` AZURE_TENANT_ID=$env:AZURE_TENANT_ID | Out-Null Write-Host "=== Graceful DB shutdown ===" -ForegroundColor Cyan -$AppUrl = "https://$APP.azurewebsites.net" if ($env:KA_NOTE_DEPLOY_API_KEY) { try { Write-Host " Waking up app..." -ForegroundColor DarkGray @@ -117,7 +132,9 @@ if ($env:KA_NOTE_DEPLOY_API_KEY) { -Headers @{ Authorization = "Bearer $env:KA_NOTE_DEPLOY_API_KEY" } ` -UseBasicParsing -TimeoutSec 30 Write-Host " Shutdown response: $($shutdownResult.message)" -ForegroundColor DarkGray - Start-Sleep -Seconds 3 + # Shutdown does WAL checkpoint synchronously before responding, then closes DB after 500ms. + # Wait 10s to ensure clean close before Azure kills the container. + Start-Sleep -Seconds 10 } catch { Write-Host " Shutdown call failed (continuing anyway): $_" -ForegroundColor Yellow } diff --git a/ka-note/server/ka-note.db-shm b/ka-note/server/ka-note.db-shm index 35530bcf02c78f4f1d2b53a1ab4548480c23f460..4e0fd2b7abd4fb02dbbc352e505a76a292169b06 100644 GIT binary patch delta 566 zcmb7-O-~b16o%h(Pp{YoQbDl>o6y;^tf+-D@?j~~uL4#DfkZkhnA92e@wxkux(Jww&bVKKFf}++&)`G*v0MpM*0?4grBR zxyu68)x^%%p=KfS=gZ0O;r`J}y7TSFd~E2rzVo*;&cFYUj2k*@N4~x5ph~SckiEM1+R*E9Il2P=}^vj67dKDsa5R2)WDz);+Rw7A>cjX4Qo zq|e|6Rs^HO9T<1J2y*u6;(?-h$^R+t!2{RjvzQRrA|1eW+!ptz^?v;`WxP=1GF}Qc z$*#RoM0Z3mEZ@d&x+!U$^a)(SvV1W~2> Fa0ouNpho}z delta 459 zcmb7;zb`{k6vxjw_Z5|(QKEjOTB=@YqKH94ipK1SpwymPPXit9=>@scM~7Ct75Ekcjcl(sM4&gWjA zQqQ%fo1|Sji+;=$Hn&dxJ8yQIGJAjiQ~-&Dny(MztSeHR)f<9trC1kTmNavhcI1J1 z#B;>=ZkH^tEX$k-p>3d2G!AvRJA}CaT2u&NPY6dLHgj+2#t`}3hO@$bA<9EA&<|GC zr>kp>d-k~<_L8TN`K;g9a~9t8DVzkT2sZ}l0F2G5gBH+and1RX!F&s+Lm7l%dncsI zDn|33BX{1>&K33@u!_(Gk}uZvT`?DzJ!|g~(LJjMn#8OViP8{Fo4-^G6)~I<&cOQI J3X>HXreClPfUE!j diff --git a/ka-note/server/ka-note.db-wal b/ka-note/server/ka-note.db-wal index 5752428e742747f0e6fcfeedebba692feb3f6939..24bc4c6db220bc657cf0378245fbb7f6c8ab3924 100644 GIT binary patch delta 1581 zcmZA12~1OW90%}sL0`|;qLiaR0XgIlo*>BNSQ+fdh9ZmwEsZN622c(eumdJlDriO& z+~CP{QXm7yAg~qqZ!(7nCW?s?flS@91F|hn5Vna<#*UvQed*TZ)AarOec%3G`|s;& zGZJ0EV8o4tksP4%Bb6AA!NcpjUive2Kw`q}?>{%3oWZ%`X+njUadp!~0$zq#58=m* z)RM><^{GfT>olnNPdBlAZEZqbnwC8iInop-j3EDz&7)lp$&X}SB1)*l*-IC)_lJoo zp)=0Do_F9tF1xl|o>#cvteuqMbZyU%C3NB6S;(Yvw>CC+U)6_PUc?Z)M`&F;e?IcX z(!%6>X49_=4Ckw)9FaOw+!tv z1FApA=M-1o&5eJQp@`e8V>S?W1k&mlPZ3E)FkIz6rgQCU`^qOE>>1tNN655d zZ=J0_KILJ4)}A>eZ|CRLuXf1UFPeWwldb}5t{_#}_-tkLAkvKSxx`jk#|OKkjtxcg zF+99uA>?|_V^loH#|$s1(z+|->=2v5=ia$do8Pp(U<{c^DJgd>x4rzr9s}i=mph93 zUlmzD{tnZcJX%4TX6US^~(!69F5o39SK_gtu!y^D6b(bj%r=5qJx-d9FBc9&fMY6zwk zGIZI52jkrJXxbRU$X$hEkyzp&k_crIMy3!c7)A)2;;3zLk4v{|G_b~(GsMv0_Bz-H zgcJIv4bNbR!*6hLpW+4T8r!@)V((OZ`dFG}dtpHs7Q~G>6YDPM`117Ew7Xm zHsXN^7#1f4#tP__;pj#Z?adIz*d`s#Im0Biw#_Y6`uLmIG}&DJDLlZ<2~KA*j7b-< zPyT#4P;F{@*QTa+>I8 z=+dHZxz^XF1`wMmw*Dx-5k0O)jxI})=>Z{^)91#Teb)7#YBw8LII6zsYZ&}tX$zWM zPr~7H-;SitcDG?v-^!o=;I|XfjWI+6Vyo1Ar8Lw zHx^XXn*BJc&n#~swzOs0w>@MT!1T30XN_d zJb)T_0x#eVHUJ;s3;cjT*a!kZAP54%U=s)dp@0ROK^O=JTR;Se1Y1EAcnWL-(I5uI Pf;jLr(4aO^e2x9T>*ocV delta 1062 zcmYk)e@v8h90%}w4&ol}x#J#1FS*Nn5Fr$fyb~=@;J}5^np`-Pk{=hu*nr881EK|a z#7zchQ9~XTXr~siy5zP)K6C2_0xied(m}(<(#z(LYPC}vwt!vluUo8Vd;Rg*`~7^r z+xK}!&bgE$;B;{=r7L7Cv~ri8JEFi7ee-iw$Pc6WH(Hy>d@mu2>>WB$3D>JjQ0W@$uGMtfM=$_&{ysWBc)UtALU8}`t|w=7N3FU16p?e8LI zhB`!)7w+n`d|#mPRIX*lJZ|Aa#PyB;KRGxlKbX?woFZwB9`*hbwU+l4JSypS7plnH zumKx=-%tM1?HMqU#ETfmyCPz{8;$YgKl=E1qxQpFV<+Rt7SGa{_T(H@E80ZP7g@Tn<>9$pLt>?T{hE*$#^op!T!S1a5D#-spQkNXCkx9eE#GyqYIovv z_CE3+Pzs{<@eQe*A7!4sb@>3*`q|!_Wpg{rK1u5(zhkx1)V=>mP1YsHMYn*)qpGZ; zQrnC~V()5WrN>-d)0bU9S0z?See=)3;1$gvS;tl@XRm+EjUN6WIG)KG_iWPUp$owu z<=Q!=kt>#u&nfqE#bj;ey*mCVS5clPc@O$*;)0HI%9=VMng1!aLi)}%b(h3J+!pVM ztC*Z`w&#<*jqO|Z$Mt>PapneDzhtSlcOcafyuCuT5A+z7$1UX}O>lsz*ob=v*d5#@qGoQM$3Dahf28%`Yf9G%XC{OKe1Mm!1urX+Wa;{ef@Yk`{V9s ze0FVzVrvQo)+J#aO}!$-&bItc+lyIR)1<14YV#P#nWe#)KKN=+`NRt`WPgXH^{*ry z91iVRCR;vxcj!c(vjo`y{zK{RZJ z7}x@_5C_jdJZyz+kN}Bbf+Tnrwu2cgumh4|C+vdf;CV=aRM-uBAPv$X1FYm#XEulZ E3-*qVPyhe` diff --git a/ka-note/server/src/routes/admin.ts b/ka-note/server/src/routes/admin.ts index d3134fd..e62f996 100644 --- a/ka-note/server/src/routes/admin.ts +++ b/ka-note/server/src/routes/admin.ts @@ -33,20 +33,37 @@ admin.get('/stats', handle('admin/stats', async (c) => { return c.json({ contextCount, topicCount }); })); +// Flush WAL to main DB file. Call before downloading the DB via Kudu. +admin.post('/checkpoint', handle('admin/checkpoint', async (c) => { + console.log('[admin] checkpoint requested'); + const result = sqlite.pragma('wal_checkpoint(FULL)', { simple: false }) as { blocking: number; log: number; checkpointed: number }[]; + const { log, checkpointed } = result[0]; + console.log(`[admin] checkpoint done: ${checkpointed}/${log} pages`); + return c.json({ ok: true, log, checkpointed }); +})); + // Graceful shutdown: checkpoint WAL, close DB, exit. // Call from deploy pipeline before restarting the container. admin.post('/shutdown', handle('admin/shutdown', async (c) => { console.log('[admin] shutdown requested via API'); + // Checkpoint synchronously before sending response — guarantees WAL is flushed + // even if the container is killed shortly after. + try { + sqlite.pragma('wal_checkpoint(TRUNCATE)'); + console.log('[admin] WAL checkpointed'); + } catch (e) { + console.error('[admin] checkpoint error during shutdown:', e); + } + // Close DB and exit after response has been flushed (500ms buffer). setTimeout(() => { try { - sqlite.pragma('wal_checkpoint(TRUNCATE)'); sqlite.close(); console.log('[admin] database closed cleanly, exiting'); } catch (e) { - console.error('[admin] error during shutdown:', e); + console.error('[admin] close error during shutdown:', e); } process.exit(0); - }, 100); + }, 500); return c.json({ ok: true, message: 'shutting down' }); }));