mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
fix(runtime): finalize and recover sandbox workspace exports safely (#14402)
Serialize native workspace finalization, validate streamed archives within bounded limits, and quietly recover unsafe exports from saved results. Preserve exact allocations for exhausted transient failures and provide export-only retry without rerunning the provider. Consolidates #14314, #14315, #14329, and #14334 while preserving the already-merged finalization label changes. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
3447609d22
commit
14795136f5
56 files changed
+3527
-143
No files matched your search
@@ -255,6 +255,19 @@ export function postgresJsOptions(options: DatabaseClientOptions): Record<string
|
||||
return driverOptions;
|
||||
}
|
||||
|
||||
// A long advisory-lock transaction must not borrow the normal pool that its
|
||||
// work needs for progress writes. Keep connection configuration private to the
|
||||
// originating Db lifetime; callers receive neither URLs nor credentials.
|
||||
const dedicatedDbFactories = new WeakMap<object, () => Db>();
|
||||
|
||||
export async function withDedicatedDbConnection<T>(db: Db, action: (dedicated: Db) => Promise<T>): Promise<T> {
|
||||
const factory = dedicatedDbFactories.get(db);
|
||||
if (!factory) throw new Error("dedicated_connection_requires_create_db");
|
||||
const dedicated = factory();
|
||||
try { return await action(dedicated); }
|
||||
finally { await dedicated.$client.end({ timeout: 1 }); }
|
||||
}
|
||||
|
||||
export function createDb(url: string, options?: DatabaseClientOptions) {
|
||||
const resolved = resolveDatabaseClientOptions(options ?? databaseClientOptionsFromEnv());
|
||||
const sql = postgres(url, postgresJsOptions(resolved));
|
||||
@@ -263,7 +276,11 @@ export function createDb(url: string, options?: DatabaseClientOptions) {
|
||||
// The registry keeps the real client (teardown must end the actual pool);
|
||||
// drizzle gets the retrying face so a pooler-recycled socket replays the
|
||||
// query instead of failing the request that happened to draw it.
|
||||
return drizzlePg(withTransientWriteRetry(sql), { schema });
|
||||
const db = drizzlePg(withTransientWriteRetry(sql), { schema });
|
||||
dedicatedDbFactories.set(db, () => createDb(url, {
|
||||
...resolved, maxConnections: 1, applicationName: "paperclip-workspace-finalization-lock",
|
||||
}));
|
||||
return db;
|
||||
}
|
||||
|
||||
export async function getPostgresDataDirectory(url: string): Promise<string | null> {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export {
|
||||
createDb,
|
||||
withDedicatedDbConnection,
|
||||
closeRegisteredClients,
|
||||
getPostgresDataDirectory,
|
||||
ensurePostgresDatabase,
|
||||
|
||||
Reference in new issue
Block a user