@mastra/pg 1.36.0-alpha.1 → 1.36.0-alpha.3

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.
package/dist/index.js CHANGED
@@ -2696,7 +2696,8 @@ async function loadSchemaSnapshot(client, schemaName) {
2696
2696
  notNullColumns,
2697
2697
  indexes,
2698
2698
  replicaIdentityIndexes,
2699
- primaryKeyIndexes
2699
+ primaryKeyIndexes,
2700
+ createdTables: /* @__PURE__ */ new Set()
2700
2701
  };
2701
2702
  }
2702
2703
  // src/storage/db/index.ts
@@ -2856,26 +2857,30 @@ function generateTableSQL({ tableName, schema, schemaName, compositePrimaryKey,
2856
2857
  ` : ""}
2857
2858
  `;
2858
2859
  }
2860
+ function indexColumnsSQL(columns, opclass) {
2861
+ return columns.map((col) => {
2862
+ const [colName, ...modifiers] = col.includes(" DESC") || col.includes(" ASC") ? col.split(" ") : [col];
2863
+ if (!colName) throw new Error(`Invalid column specification: ${col}`);
2864
+ return [
2865
+ `"${parseSqlIdentifier(colName, "column name")}"`,
2866
+ opclass,
2867
+ ...modifiers
2868
+ ].filter(Boolean).join(" ");
2869
+ }).join(", ");
2870
+ }
2859
2871
  /**
2860
2872
  * Generates a CREATE INDEX SQL statement from index options.
2861
2873
  * Used by exportSchemas to produce index DDL without a database connection.
2862
2874
  */
2863
2875
  function generateIndexSQL(options, schemaName) {
2864
- const { name, table, columns, unique = false, where, method = "btree" } = options;
2876
+ const { name, table, columns, unique = false, where, method = "btree", opclass } = options;
2865
2877
  const fullTableName = getTableName$6({
2866
2878
  indexName: table,
2867
2879
  schemaName: getSchemaName$6(schemaName)
2868
2880
  });
2869
2881
  const uniqueStr = unique ? "UNIQUE " : "";
2870
2882
  const methodStr = method !== "btree" ? `USING ${method} ` : "";
2871
- const columnsStr = columns.map((col) => {
2872
- if (col.includes(" DESC") || col.includes(" ASC")) {
2873
- const [colName, ...modifiers] = col.split(" ");
2874
- if (!colName) throw new Error(`Invalid column specification: ${col}`);
2875
- return `"${parseSqlIdentifier(colName, "column name")}" ${modifiers.join(" ")}`;
2876
- }
2877
- return `"${parseSqlIdentifier(col, "column name")}"`;
2878
- }).join(", ");
2883
+ const columnsStr = indexColumnsSQL(columns, opclass);
2879
2884
  const whereStr = where ? ` WHERE ${where}` : "";
2880
2885
  return `CREATE ${uniqueStr}INDEX IF NOT EXISTS ${`"${parseSqlIdentifier(name, "index name")}"`} ON ${fullTableName} ${methodStr}(${columnsStr})${whereStr};`;
2881
2886
  }
@@ -3386,6 +3391,7 @@ var PgDB = class extends MastraBase {
3386
3391
  if (this.schemaName) await this.setupSchema();
3387
3392
  const snapshot = this.schemaSnapshot;
3388
3393
  if (!snapshot || !this.snapshotShowsTableConverged(snapshot, tableName)) {
3394
+ const isNewTable = snapshot ? !snapshot.tables.has(tableName) : false;
3389
3395
  const sql = generateTableSQL({
3390
3396
  tableName,
3391
3397
  schema,
@@ -3399,6 +3405,7 @@ var PgDB = class extends MastraBase {
3399
3405
  }
3400
3406
  if (snapshot) {
3401
3407
  snapshot.tables.add(tableName);
3408
+ if (isNewTable) snapshot.createdTables.add(tableName);
3402
3409
  const created = this.snapshotColumns(snapshot, tableName);
3403
3410
  for (const [name, def] of Object.entries(schema)) {
3404
3411
  const parsedName = parseSqlIdentifier(name, "column name");
@@ -3867,18 +3874,9 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3867
3874
  const snapshot = this.schemaSnapshot;
3868
3875
  if (snapshot?.indexes.has(name)) return;
3869
3876
  const uniqueStr = unique ? "UNIQUE " : "";
3870
- const concurrentStr = concurrent ? "CONCURRENTLY " : "";
3877
+ const concurrentStr = concurrent && !this.isTableCreatedInThisInit(table) ? "CONCURRENTLY " : "";
3871
3878
  const methodStr = method !== "btree" ? `USING ${method} ` : "";
3872
- const columnsStr = columns.map((col) => {
3873
- if (col.includes(" DESC") || col.includes(" ASC")) {
3874
- const [colName, ...modifiers] = col.split(" ");
3875
- if (!colName) throw new Error(`Invalid column specification: ${col}`);
3876
- const quotedCol = `"${parseSqlIdentifier(colName, "column name")}" ${modifiers.join(" ")}`;
3877
- return opclass ? `${quotedCol} ${opclass}` : quotedCol;
3878
- }
3879
- const quotedCol = `"${parseSqlIdentifier(col, "column name")}"`;
3880
- return opclass ? `${quotedCol} ${opclass}` : quotedCol;
3881
- }).join(", ");
3879
+ const columnsStr = indexColumnsSQL(columns, opclass);
3882
3880
  const whereStr = where ? ` WHERE ${where}` : "";
3883
3881
  const tablespaceStr = tablespace ? ` TABLESPACE ${tablespace}` : "";
3884
3882
  let withStr = "";
@@ -3911,6 +3909,13 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3911
3909
  if (snapshot?.indexes.has(indexName)) return;
3912
3910
  if (await buildIndex(this.client, this.schemaName || "public", indexName, () => this.timedIndexBuild(indexName, sql), this.logger)) snapshot?.indexes.add(indexName);
3913
3911
  }
3912
+ /**
3913
+ * Whether `tableName` was absent when this init's snapshot was read and this
3914
+ * init created it, so its indexes can be built without CONCURRENTLY.
3915
+ */
3916
+ isTableCreatedInThisInit(tableName) {
3917
+ return this.schemaSnapshot?.createdTables.has(tableName) ?? false;
3918
+ }
3914
3919
  async timedIndexBuild(indexName, sql) {
3915
3920
  const startedAt = Date.now();
3916
3921
  this.logger.info(`Creating index ${indexName}`);
@@ -11650,6 +11655,13 @@ function dedupeMessagesForSave(messages) {
11650
11655
  }
11651
11656
  return Array.from(deduped.values());
11652
11657
  }
11658
+ /** Shallow merge; keys patched to `undefined` are dropped when serialized. Non-object metadata is replaced. */
11659
+ function mergeMetadata(existing, patch) {
11660
+ return {
11661
+ ...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {},
11662
+ ...patch
11663
+ };
11664
+ }
11653
11665
  var MemoryPG = class MemoryPG extends MemoryStorage {
11654
11666
  supportsPartialThreadUpdate = true;
11655
11667
  supportsObservationalMemory = true;
@@ -12216,7 +12228,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12216
12228
  const createdAt = toUtcISOString(thread.createdAt);
12217
12229
  const updatedAt = toUtcISOString(thread.updatedAt);
12218
12230
  const metadataJson = thread.metadata ? toPgJson(thread.metadata) : null;
12219
- await this.#fenced(fence, "saveThread", (q) => q.none(`INSERT INTO ${tableName} (
12231
+ const saved = await this.#fenced(fence, "saveThread", (q) => q.oneOrNone(`INSERT INTO ${tableName} AS t (
12220
12232
  id,
12221
12233
  "resourceId",
12222
12234
  title,
@@ -12227,13 +12239,12 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12227
12239
  "updatedAtZ"
12228
12240
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
12229
12241
  ON CONFLICT (id) DO UPDATE SET
12230
- "resourceId" = EXCLUDED."resourceId",
12231
12242
  title = EXCLUDED.title,
12232
12243
  metadata = EXCLUDED.metadata,
12233
- "createdAt" = EXCLUDED."createdAt",
12234
- "createdAtZ" = EXCLUDED."createdAtZ",
12235
12244
  "updatedAt" = EXCLUDED."updatedAt",
12236
- "updatedAtZ" = EXCLUDED."updatedAtZ"`, [
12245
+ "updatedAtZ" = EXCLUDED."updatedAtZ"
12246
+ WHERE t."resourceId" = EXCLUDED."resourceId"
12247
+ RETURNING *`, [
12237
12248
  thread.id,
12238
12249
  thread.resourceId,
12239
12250
  thread.title,
@@ -12243,12 +12254,26 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12243
12254
  updatedAt,
12244
12255
  updatedAt
12245
12256
  ]));
12257
+ if (!saved) throw new MastraError({
12258
+ id: createStorageErrorId("PG", "SAVE_THREAD", "RESOURCE_ID_MISMATCH"),
12259
+ domain: ErrorDomain.STORAGE,
12260
+ category: ErrorCategory.USER,
12261
+ text: `Thread ${thread.id} already exists and belongs to a different resource. Use updateThreadResourceId to move a thread and its messages to another resource.`,
12262
+ details: {
12263
+ threadId: thread.id,
12264
+ resourceId: thread.resourceId
12265
+ }
12266
+ });
12246
12267
  return {
12247
- ...thread,
12248
- metadata: metadataJson ? JSON.parse(metadataJson) : thread.metadata
12268
+ id: saved.id,
12269
+ resourceId: saved.resourceId,
12270
+ title: saved.title,
12271
+ metadata: saved.metadata == null ? void 0 : typeof saved.metadata === "string" ? JSON.parse(saved.metadata) : saved.metadata,
12272
+ createdAt: saved.createdAtZ || saved.createdAt,
12273
+ updatedAt: saved.updatedAtZ || saved.updatedAt
12249
12274
  };
12250
12275
  } catch (error) {
12251
- if (isRunFenceConflictError(error)) throw error;
12276
+ if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
12252
12277
  throw new MastraError({
12253
12278
  id: createStorageErrorId("PG", "SAVE_THREAD", "FAILED"),
12254
12279
  domain: ErrorDomain.STORAGE,
@@ -12257,52 +12282,125 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12257
12282
  }, error);
12258
12283
  }
12259
12284
  }
12260
- async updateThread({ id, title, metadata, fence }) {
12285
+ #toThread(row) {
12286
+ return {
12287
+ id: row.id,
12288
+ resourceId: row.resourceId,
12289
+ title: row.title,
12290
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata,
12291
+ createdAt: row.createdAtZ || row.createdAt,
12292
+ updatedAt: row.updatedAtZ || row.updatedAt
12293
+ };
12294
+ }
12295
+ async #lockThread(t, tableName, { id, resourceId }) {
12296
+ const params = [id];
12297
+ let query = `SELECT * FROM ${tableName} WHERE id = $1`;
12298
+ if (resourceId !== void 0) {
12299
+ query += ` AND "resourceId" = $2`;
12300
+ params.push(resourceId);
12301
+ }
12302
+ const row = await t.oneOrNone(`${query} FOR UPDATE`, params);
12303
+ return row ? this.#toThread(row) : null;
12304
+ }
12305
+ /**
12306
+ * Writes `title` (when given) and the full `metadata` value (when given).
12307
+ * Callers that merge metadata must hold the row lock from `#lockThread`.
12308
+ */
12309
+ async #writeThread(q, tableName, { id, title, metadata }) {
12310
+ const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12311
+ const params = [
12312
+ title ?? null,
12313
+ nowStr,
12314
+ nowStr,
12315
+ id
12316
+ ];
12317
+ let metadataSet = "";
12318
+ if (metadata !== void 0) {
12319
+ params.push(toPgJson(metadata));
12320
+ metadataSet = "metadata = $5,";
12321
+ }
12322
+ const row = await q.oneOrNone(`UPDATE ${tableName}
12323
+ SET
12324
+ title = COALESCE($1, title),
12325
+ ${metadataSet}
12326
+ "updatedAt" = $2,
12327
+ "updatedAtZ" = $3
12328
+ WHERE id = $4
12329
+ RETURNING *`, params);
12330
+ return row ? this.#toThread(row) : null;
12331
+ }
12332
+ /**
12333
+ * Cross-process atomic metadata read-modify-write: the thread row stays locked
12334
+ * (`SELECT ... FOR UPDATE`) from the read through the write, so concurrent
12335
+ * writers from any process serialize instead of overwriting each other.
12336
+ */
12337
+ async updateThreadMetadata({ id, resourceId, update, fence: explicitFence }) {
12338
+ const fence = resolveRunFence(this, explicitFence);
12339
+ const threadsTable = getTableName$3({
12340
+ indexName: TABLE_THREADS,
12341
+ schemaName: getSchemaName$3(this.#schema)
12342
+ });
12343
+ try {
12344
+ return await this.#db.client.tx(async (t) => {
12345
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateThreadMetadata");
12346
+ const thread = await this.#lockThread(t, threadsTable, {
12347
+ id,
12348
+ resourceId
12349
+ });
12350
+ if (!thread) return null;
12351
+ const metadata = update(thread);
12352
+ if (!metadata) return thread;
12353
+ return this.#writeThread(t, threadsTable, {
12354
+ id,
12355
+ metadata: mergeMetadata(thread.metadata, metadata)
12356
+ });
12357
+ });
12358
+ } catch (error) {
12359
+ if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
12360
+ throw new MastraError({
12361
+ id: createStorageErrorId("PG", "UPDATE_THREAD_METADATA", "FAILED"),
12362
+ domain: ErrorDomain.STORAGE,
12363
+ category: ErrorCategory.THIRD_PARTY,
12364
+ details: { threadId: id }
12365
+ }, error);
12366
+ }
12367
+ }
12368
+ async updateThread({ id, title, metadata, fence: explicitFence }) {
12261
12369
  const threadTableName = getTableName$3({
12262
12370
  indexName: TABLE_THREADS,
12263
12371
  schemaName: getSchemaName$3(this.#schema)
12264
12372
  });
12373
+ const notFound = () => new MastraError({
12374
+ id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
12375
+ domain: ErrorDomain.STORAGE,
12376
+ category: ErrorCategory.USER,
12377
+ text: `Thread ${id} not found`,
12378
+ details: {
12379
+ threadId: id,
12380
+ title: title ?? null
12381
+ }
12382
+ });
12265
12383
  try {
12266
- return await this.#fenced(fence, "updateThread", async (q) => {
12267
- const existingThread = await this.#getThreadById(q, { threadId: id });
12268
- if (!existingThread) throw new MastraError({
12269
- id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
12270
- domain: ErrorDomain.STORAGE,
12271
- category: ErrorCategory.USER,
12272
- text: `Thread ${id} not found`,
12273
- details: {
12274
- threadId: id,
12275
- title: title ?? null
12276
- }
12384
+ if (metadata === void 0) return await this.#fenced(explicitFence, "updateThread", async (q) => {
12385
+ const thread = await this.#writeThread(q, threadTableName, {
12386
+ id,
12387
+ title
12277
12388
  });
12278
- const mergedMetadata = {
12279
- ...existingThread.metadata,
12280
- ...metadata
12281
- };
12282
- const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12283
- const thread = await q.one(`UPDATE ${threadTableName}
12284
- SET
12285
- title = COALESCE($1, title),
12286
- metadata = $2,
12287
- "updatedAt" = $3,
12288
- "updatedAtZ" = $4
12289
- WHERE id = $5
12290
- RETURNING *
12291
- `, [
12292
- title ?? null,
12293
- mergedMetadata,
12294
- nowStr,
12295
- nowStr,
12296
- id
12297
- ]);
12298
- return {
12299
- id: thread.id,
12300
- resourceId: thread.resourceId,
12301
- title: thread.title,
12302
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
12303
- createdAt: thread.createdAtZ || thread.createdAt,
12304
- updatedAt: thread.updatedAtZ || thread.updatedAt
12305
- };
12389
+ if (!thread) throw notFound();
12390
+ return thread;
12391
+ });
12392
+ const fence = resolveRunFence(this, explicitFence);
12393
+ return await this.#db.client.tx(async (t) => {
12394
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateThread");
12395
+ const existing = await this.#lockThread(t, threadTableName, { id });
12396
+ if (!existing) throw notFound();
12397
+ const thread = await this.#writeThread(t, threadTableName, {
12398
+ id,
12399
+ title,
12400
+ metadata: mergeMetadata(existing.metadata, metadata)
12401
+ });
12402
+ if (!thread) throw notFound();
12403
+ return thread;
12306
12404
  });
12307
12405
  } catch (error) {
12308
12406
  if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
@@ -17358,8 +17456,12 @@ ${onConflict}`,
17358
17456
  async function insertRows(client, schema, table, records, partitioning, onConflict) {
17359
17457
  const insert = buildInsert(schema, table, records, onConflict);
17360
17458
  if (!insert) return;
17459
+ await withPartitionRetry(client, schema, table, records, partitioning, () => client.query(insert.text, insert.values));
17460
+ }
17461
+ /** Retry a complete write after creating a missing native partition, outside any failed transaction. */
17462
+ async function withPartitionRetry(client, schema, table, records, partitioning, write) {
17361
17463
  try {
17362
- await client.query(insert.text, insert.values);
17464
+ return await write();
17363
17465
  } catch (error) {
17364
17466
  if (!isMissingPartitionError(error) || await resolveMode(client, partitioning) !== "native") throw error;
17365
17467
  const days = withinPartitionWindow(recordDays(records, SIGNAL_TIME_COLUMN[table]), partitioning);
@@ -17372,7 +17474,7 @@ async function insertRows(client, schema, table, records, partitioning, onConfli
17372
17474
  cause
17373
17475
  }), { cause });
17374
17476
  });
17375
- await client.query(insert.text, insert.values);
17477
+ return await write();
17376
17478
  }
17377
17479
  }
17378
17480
  /** `42501`: the role may not create the child, or does not own the parent it must attach to. */
@@ -18306,13 +18408,47 @@ function pushFeedbackIdentity(acc, feedbackType, feedbackSource) {
18306
18408
  acc.conditions.push(`"valueNumber" IS NOT NULL`);
18307
18409
  }
18308
18410
  async function createFeedback(client, schema, args, partitioning) {
18309
- const row = feedbackRecordToRow(args.feedback);
18310
- await insertRows(client, schema, TABLE_FEEDBACK_EVENTS, [row], partitioning, FEEDBACK_UPSERT_CLAUSE);
18411
+ await writeFeedback(client, schema, [args.feedback], args.onConflict, partitioning);
18412
+ }
18413
+ async function replaceFeedback(client, schema, args, partitioning) {
18414
+ await writeFeedback(client, schema, [args.feedback], "replace", partitioning, true);
18311
18415
  }
18312
18416
  async function batchCreateFeedback(client, schema, args, partitioning) {
18313
- if (args.feedbacks.length === 0) return;
18314
- const rows = [...new Map(args.feedbacks.map((feedback) => [feedback.feedbackId, feedback])).values()].map(feedbackRecordToRow);
18315
- await insertRows(client, schema, TABLE_FEEDBACK_EVENTS, rows, partitioning, FEEDBACK_UPSERT_CLAUSE);
18417
+ await writeFeedback(client, schema, args.feedbacks, args.onConflict, partitioning);
18418
+ }
18419
+ /** Serialize feedback writes by ID, including absent IDs and retries with a different timestamp. */
18420
+ async function writeFeedback(client, schema, feedbacks, onConflict, partitioning, preserveIdentity = false) {
18421
+ if (!feedbacks.length) return;
18422
+ const currentFeedback = new Map((onConflict === "ignore" ? [...feedbacks].reverse() : feedbacks).map((feedback) => [feedback.feedbackId, feedback]));
18423
+ const rows = [...currentFeedback.values()].map(feedbackRecordToRow);
18424
+ const ids = [...currentFeedback.keys()].sort();
18425
+ await withPartitionRetry(client, schema, TABLE_FEEDBACK_EVENTS, rows, partitioning, () => client.tx(async (tx) => {
18426
+ await tx.query(`SELECT pg_advisory_xact_lock(hashtextextended(lock_key, 0))
18427
+ FROM unnest($1::text[]) WITH ORDINALITY AS locks(lock_key, position)
18428
+ ORDER BY position`, [ids.map((id) => JSON.stringify([
18429
+ schema,
18430
+ TABLE_FEEDBACK_EVENTS,
18431
+ id
18432
+ ]))]);
18433
+ const existing = await tx.any(`SELECT DISTINCT ON ("feedbackId") "feedbackId", "traceId", "timestamp", "organizationId", "resourceId"
18434
+ FROM ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
18435
+ WHERE "feedbackId" = ANY($1::text[]) ORDER BY "feedbackId", "cursorId" DESC`, [ids]);
18436
+ const byId = new Map(existing.map((row) => [row.feedbackId, row]));
18437
+ const accepted = rows.filter((row) => {
18438
+ const previous = byId.get(row.feedbackId);
18439
+ if (!previous) return true;
18440
+ if (onConflict === "ignore") return false;
18441
+ if (preserveIdentity && (previous.traceId !== row.traceId || new Date(previous.timestamp).getTime() !== new Date(row.timestamp).getTime() || (previous.organizationId ?? null) !== (row.organizationId ?? null) || (previous.resourceId ?? null) !== (row.resourceId ?? null))) throw new MastraError({
18442
+ id: "OBSERVABILITY_FEEDBACK_IDENTITY_CONFLICT",
18443
+ domain: ErrorDomain.MASTRA_OBSERVABILITY,
18444
+ category: ErrorCategory.USER,
18445
+ text: "Feedback replacement must preserve its trace, timestamp, and tenant"
18446
+ });
18447
+ return true;
18448
+ });
18449
+ const insert = buildInsert(schema, TABLE_FEEDBACK_EVENTS, accepted, FEEDBACK_UPSERT_CLAUSE);
18450
+ if (insert) await tx.query(insert.text, insert.values);
18451
+ }));
18316
18452
  }
18317
18453
  async function getFeedbackById(client, schema, feedbackId) {
18318
18454
  const row = await client.oneOrNone(`SELECT ${FEEDBACK_SELECT_COLUMNS} FROM ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
@@ -22003,6 +22139,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22003
22139
  "thread-query",
22004
22140
  "trace-query-tenant-scope",
22005
22141
  "feedback",
22142
+ "feedback-replacement",
22006
22143
  "trace-query-context-ids",
22007
22144
  ...contextFieldFeatures,
22008
22145
  ...timingFeatures,
@@ -22031,6 +22168,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22031
22168
  "thread-query",
22032
22169
  "trace-query-tenant-scope",
22033
22170
  "feedback",
22171
+ "feedback-replacement",
22034
22172
  "trace-query-context-ids",
22035
22173
  ...contextFieldFeatures,
22036
22174
  ...timingFeatures,
@@ -22130,6 +22268,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22130
22268
  async batchCreateScores(args) {
22131
22269
  await this.#run("BATCH_CREATE_SCORES", () => batchCreateScores(this.#client, this.#schema, args, this.#writePartitioning()), { count: args.scores.length });
22132
22270
  }
22271
+ async writeFeedbackReplacement(args) {
22272
+ await this.#run("REPLACE_FEEDBACK", () => replaceFeedback(this.#client, this.#schema, args, this.#writePartitioning()));
22273
+ }
22133
22274
  async createFeedback(args) {
22134
22275
  await this.#run("CREATE_FEEDBACK", () => createFeedback(this.#client, this.#schema, args, this.#writePartitioning()));
22135
22276
  }
@@ -22155,7 +22296,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22155
22296
  return this.#run("GET_SCORE_BY_ID", () => getScoreById(this.#readClient, this.#schema, scoreId), { scoreId });
22156
22297
  }
22157
22298
  async getFeedbackById(feedbackId) {
22158
- return this.#run("GET_FEEDBACK_BY_ID", () => getFeedbackById(this.#readClient, this.#schema, feedbackId), { feedbackId });
22299
+ return this.#run("GET_FEEDBACK_BY_ID", () => getFeedbackById(this.#client, this.#schema, feedbackId), { feedbackId });
22159
22300
  }
22160
22301
  async listFeedback(args) {
22161
22302
  return this.#run("LIST_FEEDBACK", () => listFeedback(this.#readClient, this.#schema, args));
@@ -25941,15 +26082,16 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
25941
26082
  this.logger?.warn?.(`Failed to create index ${stateBaseIndexName}:`, error);
25942
26083
  }
25943
26084
  if (await this.#db.getColumnType(TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
26085
+ const concurrently = !this.#db.isTableCreatedInThisInit(TABLE_WORKFLOW_SNAPSHOT);
25944
26086
  const indexName = workflowSnapshotStatusIndexName(this.#schema);
25945
26087
  try {
25946
- await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema, true));
26088
+ await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema, concurrently));
25947
26089
  } catch (error) {
25948
26090
  this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
25949
26091
  }
25950
26092
  const threadIdIndexName = workflowSnapshotThreadIdIndexName(this.#schema);
25951
26093
  try {
25952
- await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema, true));
26094
+ await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema, concurrently));
25953
26095
  } catch (error) {
25954
26096
  this.logger?.warn?.(`Failed to create index ${threadIdIndexName}:`, error);
25955
26097
  }