diff --git a/packages/stores/bun-sql/index.js b/packages/stores/bun-sql/index.js index 346708d..50f361f 100644 --- a/packages/stores/bun-sql/index.js +++ b/packages/stores/bun-sql/index.js @@ -30,6 +30,11 @@ export class BunSqlIdempotencyStore { */ isMySQL; + /** + * @type {boolean} + */ + isPostgres; + /** * @param {string} connectionString - Database connection string or path * @param {BunSqlIdempotencyStoreOptions} [options] @@ -37,6 +42,7 @@ export class BunSqlIdempotencyStore { constructor(connectionString, options = {}) { this.isSqlite = this.isSqliteConnection(connectionString); this.isMySQL = this.isMySqlConnection(connectionString); + this.isPostgres = this.isPostgresConnection(connectionString); if (this.isSqlite) { const sqlitePath = this.normalizeSqlitePath(connectionString); @@ -118,6 +124,17 @@ export class BunSqlIdempotencyStore { return lower.includes("mysql") || lower.includes("mariadb"); } + /** + * Check if connection string is PostgreSQL + * @param {string} [connectionString] + * @returns {boolean} + */ + isPostgresConnection(connectionString) { + if (!connectionString) return false; + const lower = connectionString.toLowerCase(); + return lower.includes("postgres") || lower.includes("postgresql"); + } + /** * Initialize database schema with tables and indexes * @private @@ -261,8 +278,12 @@ export class BunSqlIdempotencyStore { byFingerprint: this.parseRecord(byFingerprintRow) }; } else { - await this - .db`DELETE FROM idempotency_records WHERE expires_at <= ${Date.now()}`; + // PostgreSQL doesn't support LIMIT in DELETE, use subquery + const deleteSql = this.isPostgres + ? "DELETE FROM idempotency_records WHERE key IN (SELECT key FROM idempotency_records WHERE expires_at <= $1 LIMIT 10)" + : "DELETE FROM idempotency_records WHERE expires_at <= ? LIMIT 10"; + const deleteParams = this.isPostgres ? [Date.now()] : [Date.now()]; + await this.db.unsafe(deleteSql, deleteParams); const keyColumn = this.isMySQL ? "`key`" : '"key"'; const paramPlaceholder = this.isMySQL ? "?" : "$1"; diff --git a/packages/stores/mysql/deno-mysql.js b/packages/stores/mysql/deno-mysql.js index 38ec2d0..8492846 100644 --- a/packages/stores/mysql/deno-mysql.js +++ b/packages/stores/mysql/deno-mysql.js @@ -126,7 +126,7 @@ export class MysqlIdempotencyStore { */ async lookup(key, fingerprint) { await this.client.execute( - "DELETE FROM idempotency_records WHERE expires_at <= ?", + "DELETE FROM idempotency_records WHERE expires_at <= ? LIMIT 10", [Date.now()] ); diff --git a/packages/stores/mysql/node-mysql.js b/packages/stores/mysql/node-mysql.js index a1253c8..01648db 100644 --- a/packages/stores/mysql/node-mysql.js +++ b/packages/stores/mysql/node-mysql.js @@ -89,7 +89,7 @@ export class MysqlIdempotencyStore { */ async lookup(key, fingerprint) { await this.pool.query( - `DELETE FROM \`${this.tableName}\` WHERE expires_at <= ?`, + `DELETE FROM \`${this.tableName}\` WHERE expires_at <= ? LIMIT 10`, [Date.now()] ); diff --git a/packages/stores/postgres/index.js b/packages/stores/postgres/index.js index 2af46a2..a9dda09 100644 --- a/packages/stores/postgres/index.js +++ b/packages/stores/postgres/index.js @@ -113,8 +113,9 @@ export class PostgresIdempotencyStore { * @returns {Promise<{byKey: IdempotencyRecord | null, byFingerprint: IdempotencyRecord | null}>} */ async lookup(key, fingerprint) { + // Use subquery with LIMIT since PostgreSQL doesn't support LIMIT in DELETE directly await this.pool.query( - `DELETE FROM ${this.quotedSchemaIdentifier}.idempotency_records WHERE expires_at <= $1`, + `DELETE FROM ${this.quotedSchemaIdentifier}.idempotency_records WHERE key IN (SELECT key FROM ${this.quotedSchemaIdentifier}.idempotency_records WHERE expires_at <= $1 LIMIT 10)`, [Date.now()] );