diff --git a/apps/cli/src/shared/services/Dockerfile b/apps/cli/src/shared/services/Dockerfile index 533c7762ce..c4ed418420 100644 --- a/apps/cli/src/shared/services/Dockerfile +++ b/apps/cli/src/shared/services/Dockerfile @@ -7,7 +7,7 @@ # adjacent lines, and catalog PRs for neighbouring aliases are often open at the same time. FROM supabase/postgres:17.11.0.005 AS pg -FROM supabase/postgres:15.19.0.004 AS pg15 +FROM supabase/postgres:15.19.0.005 AS pg15 # Postgres 14 has no slim build; bumped by hand (Dependabot ignores supabase/postgres). FROM supabase/postgres:14.1.0.89 AS pg14 # New always-on service alias: extend SERVICE_IMAGE_ALIASES in diff --git a/packages/stack/src/Artifacts.ts b/packages/stack/src/Artifacts.ts index a1fbc161a3..8677ee5e70 100644 --- a/packages/stack/src/Artifacts.ts +++ b/packages/stack/src/Artifacts.ts @@ -144,24 +144,24 @@ const definitions: Readonly> = { "bin/psql", ], { - "15.19.0.004": { - upstreamVersion: "15.19.0.004", - revision: 1, + "15.19.0.005": { + upstreamVersion: "15.19.0.005", + revision: 0, image: - "ghcr.io/supabase/cli/postgres:15.19.0.004-r1@sha256:df77a34bf5839149c74b24a12228ad58e492c2a9d76d3887ef4f279d06173e09", - upstreamImage: "supabase/postgres:15.19.0.004", + "ghcr.io/supabase/cli/postgres:15.19.0.005-r0@sha256:8c99be31392a22ba25f6a0fe03e772ff9e1141a76044bd28578bf245f6e93d44", + upstreamImage: "supabase/postgres:15.19.0.005", natives: { "darwin-arm64": { - archive: "57463faeb424d11296a833ea271881ba4ab31434ec90b860b104e2e814de25fd", - manifest: "7a0dab606017ccfea22f1e32f69b78792f3ff5eca13e3880a3de8884e9c03832", + archive: "f308f7727280b40641da4266cf93b4dfcc8f351ff5c085fdbf0316d6e628f7c3", + manifest: "d36777085898fe0ee4fbdd1b54dc971ed58ab314367493f5b74e4e5e7bd702d8", }, "linux-amd64": { - archive: "867fba08024fb813dcbd8d0cfcc87f02dfb49d6093e18a9459a6f4c1b44236c2", - manifest: "eab1b6d62684aef3f7d3aa9e55b9e8dbf99f732e4ac23f704e75474321bdfa30", + archive: "c8734fe7c013712909de297abe1dfb28ccb74a2f6d71f0af187c074c242d5cce", + manifest: "569a9ec7f820c55a05ea84b9c09891a83b0d28f2e630a950e385ca59e34dd6ad", }, "linux-arm64": { - archive: "73502a47bb7419f70668feeb857fe2da039fdaade60a7baba41b1edb954b2796", - manifest: "3b957388f0bf05c230b1d65691412150239aa39847e0d782a1448d56258f9ef8", + archive: "5839443e6674ce706e388a928878cd4e44c91943d44aa3417a3082495200c42c", + manifest: "e19dbcab90d4033452bca4de0916c83f6851162c09fb1a10233ad86ad7ef35d1", }, }, }, @@ -186,6 +186,27 @@ const definitions: Readonly> = { }, }, }, + "17.11.0.005-orioledb": { + upstreamVersion: "17.11.0.005-orioledb", + revision: 0, + image: + "ghcr.io/supabase/cli/postgres:17.11.0.005-orioledb-r0@sha256:aa141c2d1f9b0eb2e96681781f6ff2226f6f29466308e660be565b7eb781462f", + upstreamImage: "supabase/postgres:17.11.0.005-orioledb", + natives: { + "darwin-arm64": { + archive: "5526b890ac015f4714f6dee460acc9aa0687e35f156cf437544f16fdfe812ba7", + manifest: "dde250a7063b05ea39329b00911397976357a037c3446fa1f6b6a66e5a49cb65", + }, + "linux-amd64": { + archive: "a5ebf59e43cd247819f2ce6a43cd3481baf2caf2492d39f04f1484c1bc71f499", + manifest: "6611a0d4e0e78faa477e00b43f48737be126ed8f2484f2ebf95658b8b2bdaefa", + }, + "linux-arm64": { + archive: "772b2324480bff79fdc4fd0c018b7c23295b63566997db3e65cafc51178a9f08", + manifest: "1d1ad6a4c30b8417880ba818cdd4dc29e69acd87a5c874e1e766be025636b954", + }, + }, + }, }, ), rest: definition( diff --git a/packages/stack/src/HttpProxy.integration.test.ts b/packages/stack/src/HttpProxy.integration.test.ts index d358465e99..f193f157f2 100644 --- a/packages/stack/src/HttpProxy.integration.test.ts +++ b/packages/stack/src/HttpProxy.integration.test.ts @@ -1367,6 +1367,47 @@ const rawUpgrade = (port: number, path: string) => Effect.timeout("5 seconds"), ); +/** Writes `text`, if any, on a raw client connection and waits for the next response head. */ +const responseHead = (socket: Socket, text?: string) => + Effect.callback((resume) => { + let received = ""; + const cleanup = () => { + socket.off("data", onData); + socket.off("close", onClose); + }; + const onData = (chunk: Buffer) => { + received += chunk.toString(); + if (!received.includes("\r\n\r\n")) return; + cleanup(); + resume(Effect.succeed(received)); + }; + const onClose = () => { + cleanup(); + resume(Effect.fail(new HttpProxyTestError({ message: "Gateway closed the connection" }))); + }; + socket.on("data", onData); + socket.once("close", onClose); + if (socket.destroyed) onClose(); + else if (text !== undefined) socket.write(text); + return Effect.sync(cleanup); + }).pipe(Effect.timeout("5 seconds")); + +it.live( + "answers a client that reuses its connection after stalling past the advertised keep-alive", + () => + Effect.scoped( + Effect.gen(function* () { + const proxy = yield* makeHttpProxy({ host: "127.0.0.1", port: 0 }); + const get = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; + const socket = yield* rawClient(proxy.port, get); + expect(yield* responseHead(socket)).toMatch(/keep-alive: timeout=5\r\n/iu); + /* Real time: the idle gap must outlast the runtime's default 6 s close. */ + yield* Effect.sleep("7 seconds"); + expect(yield* responseHead(socket, get)).toMatch(/^HTTP\/1\.1 404 /u); + }), + ), +); + it.live( "records each request once with its sent status, body bytes and redacted credentials", () => { diff --git a/packages/stack/src/HttpProxy.ts b/packages/stack/src/HttpProxy.ts index bdbdcdd6de..803196e53c 100644 --- a/packages/stack/src/HttpProxy.ts +++ b/packages/stack/src/HttpProxy.ts @@ -613,7 +613,9 @@ export const makeHttpProxy = (options: { const runRequest = yield* FiberSet.makeRuntime(); const sockets = new Set(); const onAccess = options.onAccess; - const server = createServer((request, response) => { + /* Advertising 5 s lets clients close idle sockets first; holding them 75 s, like Kong, keeps + a client that stalls past its own timer from reusing a closed one. */ + const server = createServer({ keepAliveTimeoutBuffer: 70_000 }, (request, response) => { runRequest( Effect.gen(function* () { const sent: Sent = { bytes: 0, clientLeft: false };