orez-sync-cf-host 0.16.7 → 0.16.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
Binary file
|
package/dist/host.js
CHANGED
|
@@ -531,7 +531,13 @@ export function createSyncDurableObject(config) {
|
|
|
531
531
|
const recordRowsRead = (rows) => {
|
|
532
532
|
this.#sqlBilling.rowsRead += rows;
|
|
533
533
|
};
|
|
534
|
-
this.#engineDb = new SqlStorageSyncDb(ctx.storage.sql, recordRowsWritten, recordRowsRead)
|
|
534
|
+
this.#engineDb = new SqlStorageSyncDb(ctx.storage.sql, recordRowsWritten, recordRowsRead, (failure) => {
|
|
535
|
+
console.error(JSON.stringify({
|
|
536
|
+
event: 'sync_sql_failure',
|
|
537
|
+
hostVersion: config.hostVersion,
|
|
538
|
+
...failure,
|
|
539
|
+
}));
|
|
540
|
+
});
|
|
535
541
|
this.#directSql = new SqlStorageDirect(ctx.storage.sql, recordRowsWritten, recordRowsRead);
|
|
536
542
|
this.#mutatorSql = new SqlStorageMutatorTransaction(this.#directSql, (ast, format) => this.#wasm(() => compileQuery(ast, format)), config.transactionQueryBudget);
|
|
537
543
|
const database = {
|
|
@@ -17,6 +17,22 @@ export type AdapterStats = {
|
|
|
17
17
|
queryCalls: number;
|
|
18
18
|
sqlMs: number;
|
|
19
19
|
};
|
|
20
|
+
export type SqlStorageFailureBinding = {
|
|
21
|
+
index: number;
|
|
22
|
+
kind: WireValue['kind'];
|
|
23
|
+
value?: string | number | null;
|
|
24
|
+
bytes?: number;
|
|
25
|
+
truncated?: true;
|
|
26
|
+
};
|
|
27
|
+
export type SqlStorageFailure = {
|
|
28
|
+
operation: 'exec' | 'query';
|
|
29
|
+
error: string;
|
|
30
|
+
sql: string;
|
|
31
|
+
sqlBytes: number;
|
|
32
|
+
sqlTruncated?: true;
|
|
33
|
+
paramCount: number;
|
|
34
|
+
params: SqlStorageFailureBinding[];
|
|
35
|
+
};
|
|
20
36
|
export declare function assertHostSql(sql: string): void;
|
|
21
37
|
export declare function decodeSqlParams(value: unknown): unknown[];
|
|
22
38
|
/**
|
|
@@ -28,8 +44,9 @@ export declare class SqlStorageSyncDb implements JsSyncDb {
|
|
|
28
44
|
private readonly sql;
|
|
29
45
|
private readonly recordRowsWritten;
|
|
30
46
|
private readonly recordRowsRead;
|
|
47
|
+
private readonly recordFailure;
|
|
31
48
|
readonly stats: AdapterStats;
|
|
32
|
-
constructor(sql: SqlStorage, recordRowsWritten?: (rows: number) => void, recordRowsRead?: (rows: number) => void);
|
|
49
|
+
constructor(sql: SqlStorage, recordRowsWritten?: (rows: number) => void, recordRowsRead?: (rows: number) => void, recordFailure?: (failure: SqlStorageFailure) => void);
|
|
33
50
|
resetStats(): void;
|
|
34
51
|
exec(sql: string, params: WireValue[]): void;
|
|
35
52
|
query(sql: string, params: WireValue[]): WireRow[];
|
|
@@ -90,6 +90,45 @@ export function decodeSqlParams(value) {
|
|
|
90
90
|
function encodeResult(value) {
|
|
91
91
|
return encodeSqlValue(value);
|
|
92
92
|
}
|
|
93
|
+
const MAX_FAILURE_TEXT_BYTES = 256;
|
|
94
|
+
const MAX_FAILURE_SQL_BYTES = 64 * 1024;
|
|
95
|
+
const MAX_FAILURE_BINDINGS = 512;
|
|
96
|
+
const failureTextEncoder = new TextEncoder();
|
|
97
|
+
function describeFailureBinding(value, index) {
|
|
98
|
+
if (value.kind === 'null')
|
|
99
|
+
return { index, kind: value.kind, value: null };
|
|
100
|
+
if (value.kind === 'blob')
|
|
101
|
+
return { index, kind: value.kind, bytes: value.value.length };
|
|
102
|
+
if (value.kind !== 'text')
|
|
103
|
+
return { index, kind: value.kind, value: value.value };
|
|
104
|
+
const bytes = failureTextEncoder.encode(value.value).byteLength;
|
|
105
|
+
if (bytes <= MAX_FAILURE_TEXT_BYTES) {
|
|
106
|
+
return { index, kind: value.kind, value: value.value, bytes };
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
index,
|
|
110
|
+
kind: value.kind,
|
|
111
|
+
value: `${value.value.slice(0, MAX_FAILURE_TEXT_BYTES / 2)}…${value.value.slice(-MAX_FAILURE_TEXT_BYTES / 2)}`,
|
|
112
|
+
bytes,
|
|
113
|
+
truncated: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function describeSqlFailure(operation, error, sql, params) {
|
|
117
|
+
const sqlBytes = failureTextEncoder.encode(sql).byteLength;
|
|
118
|
+
const failure = {
|
|
119
|
+
operation,
|
|
120
|
+
error: String(error),
|
|
121
|
+
sql: sqlBytes <= MAX_FAILURE_SQL_BYTES
|
|
122
|
+
? sql
|
|
123
|
+
: `${sql.slice(0, MAX_FAILURE_SQL_BYTES / 2)}…${sql.slice(-MAX_FAILURE_SQL_BYTES / 2)}`,
|
|
124
|
+
sqlBytes,
|
|
125
|
+
paramCount: params.length,
|
|
126
|
+
params: params.slice(0, MAX_FAILURE_BINDINGS).map(describeFailureBinding),
|
|
127
|
+
};
|
|
128
|
+
if (sqlBytes > MAX_FAILURE_SQL_BYTES)
|
|
129
|
+
failure.sqlTruncated = true;
|
|
130
|
+
return failure;
|
|
131
|
+
}
|
|
93
132
|
/**
|
|
94
133
|
* The sole JS SQL adapter used by wasm. It is synchronous, consumes every
|
|
95
134
|
* cursor before returning, accepts positional `?` only, and never starts or
|
|
@@ -99,11 +138,13 @@ export class SqlStorageSyncDb {
|
|
|
99
138
|
sql;
|
|
100
139
|
recordRowsWritten;
|
|
101
140
|
recordRowsRead;
|
|
141
|
+
recordFailure;
|
|
102
142
|
stats = { execCalls: 0, queryCalls: 0, sqlMs: 0 };
|
|
103
|
-
constructor(sql, recordRowsWritten = () => { }, recordRowsRead = () => { }) {
|
|
143
|
+
constructor(sql, recordRowsWritten = () => { }, recordRowsRead = () => { }, recordFailure = () => { }) {
|
|
104
144
|
this.sql = sql;
|
|
105
145
|
this.recordRowsWritten = recordRowsWritten;
|
|
106
146
|
this.recordRowsRead = recordRowsRead;
|
|
147
|
+
this.recordFailure = recordFailure;
|
|
107
148
|
}
|
|
108
149
|
resetStats() {
|
|
109
150
|
this.stats.execCalls = 0;
|
|
@@ -113,21 +154,34 @@ export class SqlStorageSyncDb {
|
|
|
113
154
|
exec(sql, params) {
|
|
114
155
|
assertHostSql(sql);
|
|
115
156
|
const start = performance.now();
|
|
116
|
-
|
|
157
|
+
try {
|
|
158
|
+
trackBillableCursorRows(this.sql.exec(sql, ...params.map((value) => decodeBinding(value))), this.recordRowsWritten, this.recordRowsRead);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.recordFailure(describeSqlFailure('exec', error, sql, params));
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
117
164
|
this.stats.execCalls++;
|
|
118
165
|
this.stats.sqlMs += performance.now() - start;
|
|
119
166
|
}
|
|
120
167
|
query(sql, params) {
|
|
121
168
|
assertHostSql(sql);
|
|
122
169
|
const start = performance.now();
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
170
|
+
let rows;
|
|
171
|
+
try {
|
|
172
|
+
const cursor = trackBillableCursorRows(this.sql.exec(sql, ...params.map((value) => decodeBinding(value))), this.recordRowsWritten, this.recordRowsRead);
|
|
173
|
+
const columns = [...cursor.columnNames];
|
|
174
|
+
// A SqlStorage cursor must be fully consumed before any await. Returning a
|
|
175
|
+
// materialized array here makes that property structural.
|
|
176
|
+
rows = Array.from(cursor.raw()).map((values) => ({
|
|
177
|
+
columns,
|
|
178
|
+
values: values.map(encodeResult),
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
this.recordFailure(describeSqlFailure('query', error, sql, params));
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
131
185
|
this.stats.queryCalls++;
|
|
132
186
|
this.stats.sqlMs += performance.now() - start;
|
|
133
187
|
return rows;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez-sync-cf-host",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.8",
|
|
4
4
|
"description": "Internal orez Cloudflare sync host. Do not import directly: consumers use orez-lite/cloudflare/sync.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@opentelemetry/api": "^1.9.0",
|
|
63
|
-
"orez-sync-executor": "0.16.
|
|
63
|
+
"orez-sync-executor": "0.16.8"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@cloudflare/workers-types": "4.20260617.1",
|