Skip to content

Commit 31c5950

Browse files
committed
fix(workflows): replace libsql client + replace node-fetch with native
1 parent 241eae7 commit 31c5950

9 files changed

Lines changed: 108 additions & 73 deletions

File tree

apps/workflows/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ COPY \
7373
"/app/packages" "/app/packages"
7474
RUN <<EOF
7575
mkdir -p /tmp/sc
76-
deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:v("libsql"),"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
76+
deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};const lib=v("libsql");Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:lib,"better-sqlite3":"npm:libsql@"+lib,"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
7777
cd /tmp/sc && deno install --node-modules-dir=auto --allow-scripts
78-
cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
78+
cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external 'better-sqlite3' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
7979
cd /tmp/sc && deno compile --no-check -A --node-modules-dir=manual --output /app/apps/workflows/app /tmp/sc/_serve.bundle.mjs
8080
mkdir -p /app/data
8181
EOF

apps/workflows/dofigen.lock

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,13 @@ effective: |
1212
- /packages/error
1313
- /packages/tracker
1414
builders:
15-
build:
16-
fromImage:
17-
path: denoland/deno
18-
digest: sha256:8d24854de78a79c56e74b49aa4c5996c60e1fe3730efba8fbdd2692c582e6e29
19-
label:
20-
org.opencontainers.image.base.name: docker.io/denoland/deno:2.9.0
21-
org.opencontainers.image.base.digest: sha256:8d24854de78a79c56e74b49aa4c5996c60e1fe3730efba8fbdd2692c582e6e29
22-
org.opencontainers.image.stage: build
23-
workdir: /app/apps/workflows
24-
env:
25-
NODE_ENV: production
26-
copy:
27-
- paths:
28-
- .
29-
target: /app/
30-
- fromBuilder: install
31-
paths:
32-
- /app/node_modules
33-
target: /app/node_modules
34-
- fromBuilder: install
35-
paths:
36-
- /app/apps/workflows/node_modules
37-
target: /app/apps/workflows/node_modules
38-
- fromBuilder: install
39-
paths:
40-
- /app/packages
41-
target: /app/packages
42-
run:
43-
- mkdir -p /tmp/sc
44-
- deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:v("libsql"),"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
45-
- cd /tmp/sc && deno install --node-modules-dir=auto --allow-scripts
46-
- cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
47-
- cd /tmp/sc && deno compile --no-check -A --node-modules-dir=manual --output /app/apps/workflows/app /tmp/sc/_serve.bundle.mjs
48-
- mkdir -p /app/data
4915
install:
5016
fromImage:
5117
path: node
5218
digest: sha256:b31e7a42fdf8b8aa5f5ed477c72d694301273f1069c5a2f71d53c6482e99a2fc
5319
label:
54-
org.opencontainers.image.base.digest: sha256:b31e7a42fdf8b8aa5f5ed477c72d694301273f1069c5a2f71d53c6482e99a2fc
5520
org.opencontainers.image.stage: install
21+
org.opencontainers.image.base.digest: sha256:b31e7a42fdf8b8aa5f5ed477c72d694301273f1069c5a2f71d53c6482e99a2fc
5622
org.opencontainers.image.base.name: docker.io/node:24-slim
5723
workdir: /app/
5824
run:
@@ -125,19 +91,53 @@ effective: |
12591
source: packages/locales/package.json
12692
- target: packages/status-fetcher/package.json
12793
source: packages/status-fetcher/package.json
94+
build:
95+
fromImage:
96+
path: denoland/deno
97+
digest: sha256:8d24854de78a79c56e74b49aa4c5996c60e1fe3730efba8fbdd2692c582e6e29
98+
label:
99+
org.opencontainers.image.base.name: docker.io/denoland/deno:2.9.0
100+
org.opencontainers.image.base.digest: sha256:8d24854de78a79c56e74b49aa4c5996c60e1fe3730efba8fbdd2692c582e6e29
101+
org.opencontainers.image.stage: build
102+
workdir: /app/apps/workflows
103+
env:
104+
NODE_ENV: production
105+
copy:
106+
- paths:
107+
- .
108+
target: /app/
109+
- fromBuilder: install
110+
paths:
111+
- /app/node_modules
112+
target: /app/node_modules
113+
- fromBuilder: install
114+
paths:
115+
- /app/apps/workflows/node_modules
116+
target: /app/apps/workflows/node_modules
117+
- fromBuilder: install
118+
paths:
119+
- /app/packages
120+
target: /app/packages
121+
run:
122+
- mkdir -p /tmp/sc
123+
- deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};const lib=v("libsql");Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:lib,"better-sqlite3":"npm:libsql@"+lib,"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
124+
- cd /tmp/sc && deno install --node-modules-dir=auto --allow-scripts
125+
- cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external 'better-sqlite3' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
126+
- cd /tmp/sc && deno compile --no-check -A --node-modules-dir=manual --output /app/apps/workflows/app /tmp/sc/_serve.bundle.mjs
127+
- mkdir -p /app/data
128128
fromImage:
129129
host: registry.access.redhat.com
130130
port: 443
131131
path: hi/curl
132132
digest: sha256:848b81ab5d5e55371d7193fd4f1ea7b605d14dbb039344ffad34a4c1f0d880f4
133133
label:
134+
org.opencontainers.image.base.digest: sha256:848b81ab5d5e55371d7193fd4f1ea7b605d14dbb039344ffad34a4c1f0d880f4
134135
io.dofigen.version: 2.8.0
135136
org.opencontainers.image.source: https://github.com/openstatusHQ/openstatus
136137
org.opencontainers.image.title: OpenStatus Workflows
137138
org.opencontainers.image.authors: OpenStatus Team
138-
org.opencontainers.image.description: Background job processing and probe scheduling for OpenStatus
139139
org.opencontainers.image.vendor: OpenStatus
140-
org.opencontainers.image.base.digest: sha256:848b81ab5d5e55371d7193fd4f1ea7b605d14dbb039344ffad34a4c1f0d880f4
140+
org.opencontainers.image.description: Background job processing and probe scheduling for OpenStatus
141141
user:
142142
user: '1000'
143143
group: '1000'
@@ -174,7 +174,7 @@ images:
174174
digest: sha256:b31e7a42fdf8b8aa5f5ed477c72d694301273f1069c5a2f71d53c6482e99a2fc
175175
resources:
176176
dofigen.yml:
177-
hash: 3d98c3f4837321ccc3810b1644c53d6c17f057fb57ec5d7beafa6b3caef9f59b
177+
hash: f867c1863629aad9394b8d9a4cec2bdd234fcd09663a01f5c401bd39ce5b53c2
178178
content: |
179179
ignore:
180180
- node_modules
@@ -254,15 +254,17 @@ resources:
254254
target: /app/packages
255255
env:
256256
NODE_ENV: production
257-
# Bundle the app to self-contained JS, EXTERNALISING the 3 packages that can't be inlined:
258-
# @libsql/client + libsql (native .node loader) and @google-cloud/tasks (google-proto-files reads
259-
# files via CJS __dirname). They ship as a minimal side-car node_modules (deno install, ~46MB,
260-
# platform-aware so Fly amd64 works) instead of embedding all node_modules. Binary ~146MB vs ~360MB.
257+
# Bundle the app to self-contained JS, EXTERNALISING the packages that can't be inlined:
258+
# @libsql/client + libsql + better-sqlite3 (alias of libsql, native .node loader) and
259+
# @google-cloud/tasks (google-proto-files reads files via CJS __dirname). They ship as a
260+
# minimal side-car node_modules (deno install, platform-aware so Fly amd64 works) instead
261+
# of embedding all node_modules. better-sqlite3 is the name drizzle-orm/better-sqlite3
262+
# requires; it's aliased to libsql so both resolve to the same native package.
261263
run:
262264
- mkdir -p /tmp/sc
263-
- deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:v("libsql"),"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
265+
- deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};const lib=v("libsql");Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:lib,"better-sqlite3":"npm:libsql@"+lib,"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
264266
- cd /tmp/sc && deno install --node-modules-dir=auto --allow-scripts
265-
- cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
267+
- cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external 'better-sqlite3' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
266268
- cd /tmp/sc && deno compile --no-check -A --node-modules-dir=manual --output /app/apps/workflows/app /tmp/sc/_serve.bundle.mjs
267269
- mkdir -p /app/data
268270

apps/workflows/dofigen.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,17 @@ builders:
7676
target: /app/packages
7777
env:
7878
NODE_ENV: production
79-
# Bundle the app to self-contained JS, EXTERNALISING the 3 packages that can't be inlined:
80-
# @libsql/client + libsql (native .node loader) and @google-cloud/tasks (google-proto-files reads
81-
# files via CJS __dirname). They ship as a minimal side-car node_modules (deno install, ~46MB,
82-
# platform-aware so Fly amd64 works) instead of embedding all node_modules. Binary ~146MB vs ~360MB.
79+
# Bundle the app to self-contained JS, EXTERNALISING the packages that can't be inlined:
80+
# @libsql/client + libsql + better-sqlite3 (alias of libsql, native .node loader) and
81+
# @google-cloud/tasks (google-proto-files reads files via CJS __dirname). They ship as a
82+
# minimal side-car node_modules (deno install, platform-aware so Fly amd64 works) instead
83+
# of embedding all node_modules. better-sqlite3 is the name drizzle-orm/better-sqlite3
84+
# requires; it's aliased to libsql so both resolve to the same native package.
8385
run:
8486
- mkdir -p /tmp/sc
85-
- deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:v("libsql"),"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
87+
- deno eval 'const p="/app/node_modules/.pnpm";const v=(x)=>{for(const e of Deno.readDirSync(p))if(e.name.startsWith(x+"@"))return e.name.slice(x.length+1).split("_")[0]};const lib=v("libsql");Deno.writeTextFileSync("/tmp/sc/package.json",JSON.stringify({dependencies:{"@libsql/client":v("@libsql+client"),libsql:lib,"better-sqlite3":"npm:libsql@"+lib,"@google-cloud/tasks":v("@google-cloud+tasks")}}))'
8688
- cd /tmp/sc && deno install --node-modules-dir=auto --allow-scripts
87-
- cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
89+
- cd /app/apps/workflows && deno bundle --platform=deno --minify --sloppy-imports --node-modules-dir=manual --external '@libsql/client' --external 'libsql' --external 'better-sqlite3' --external '@google-cloud/tasks' --output /tmp/sc/_serve.bundle.mjs src/serve.ts
8890
- cd /tmp/sc && deno compile --no-check -A --node-modules-dir=manual --output /app/apps/workflows/app /tmp/sc/_serve.bundle.mjs
8991
- mkdir -p /app/data
9092

apps/workflows/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@
4040
"@opentelemetry/semantic-conventions": "catalog:",
4141
"@sentry/deno": "catalog:",
4242
"@upstash/qstash": "catalog:",
43+
"better-sqlite3": "npm:libsql@0.5.29",
4344
"drizzle-orm": "catalog:",
4445
"effect": "catalog:",
4546
"hono": "catalog:",
47+
"libsql": "0.5.29",
4648
"limiter": "catalog:",
4749
"zod": "catalog:"
4850
},

apps/workflows/src/lib/db.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
import { createClient } from "@libsql/client";
21
import { schema } from "@openstatus/db";
3-
import { drizzle } from "drizzle-orm/libsql";
2+
import { drizzle } from "drizzle-orm/better-sqlite3";
3+
import Database from "libsql";
44

55
import { env } from "../env";
66

7+
// Use the `libsql` client (better-sqlite3 API) rather than `@libsql/client`: the
8+
// latter rebuilds every result row via Object.defineProperty, ~3x more CPU on
9+
// Deno's V8. Same libsql engine + embedded replica, but plain-object rows.
710
const file =
8-
env().NODE_ENV === "development" ? "./dev.db" : "///app/data/replica.db";
9-
const client = createClient({
10-
url: `file:${file}`,
11+
env().NODE_ENV === "development" ? "./dev.db" : "/app/data/replica.db";
12+
13+
// libsql's typings (better-sqlite3-derived) omit its embedded-replica options;
14+
// they're read at runtime (see libsql/index.js).
15+
type ReplicaOptions = Database.Options & {
16+
authToken?: string;
17+
syncPeriod?: number;
18+
};
19+
20+
const options: ReplicaOptions = {
1121
syncUrl: env().DATABASE_URL,
1222
authToken: env().DATABASE_AUTH_TOKEN,
13-
syncInterval: 60,
14-
});
23+
syncPeriod: 60,
24+
};
1525

16-
export const db = drizzle({
17-
client: client,
18-
schema,
19-
});
26+
export const db = drizzle(new Database(file, options), { schema });
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// google-gax (Cloud Tasks) routes through node-fetch unless `window.fetch` exists,
2+
// and node-fetch on Deno's node:http shim costs ~5x the CPU of native fetch.
3+
// Aliasing window to globalThis exposes window.fetch so google-gax picks native fetch.
4+
// It must be globalThis (not a bare {fetch}) so @t3-oss/env's `"Deno" in window`
5+
// server-detection still sees Deno and treats this as a server, not the browser.
6+
(globalThis as { window?: unknown }).window ??= globalThis;

apps/workflows/src/serve.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/// <reference lib="deno.ns" />
22

3+
import "./lib/native-fetch";
4+
35
import { getLogger } from "@logtape/logtape";
46

57
import { env } from "./env";

deno.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)