Skip to content

Benchmark

Measures the overhead of automatic WHERE deleted_at IS NULL filtering and the cost of soft-delete operations compared to hard deletes.

What We Measure

BenchmarkDescription
A) findMany — no extensionBaseline query returning all rows (including soft-deleted)
B) findMany — with soft-delete filterSame query with automatic deleted_at IS NULL injection
C) delete — hard deleteBaseline DELETE FROM
D) delete — soft deleteUPDATE SET deleted_at = now()
E) cascade soft-deleteUser + 3 Posts + 6 Comments deleted in cascade

Test Setup

  • Database: PostgreSQL 16 (Docker, port 5432)
  • Data: 500 users (half soft-deleted) for read benchmarks, fresh rows for delete benchmarks
  • Warmup: 30 iterations (discarded)
  • Measured: 300 iterations per benchmark (50 for cascade)

Running Locally

bash
# Start PostgreSQL
docker compose up -d

# Generate Prisma client & run benchmark
DATABASE_URL=postgresql://test:test@localhost:5432/soft_delete_test \
  npx prisma generate --schema=test/prisma/schema.prisma && \
  npx ts-node benchmarks/soft-delete-overhead.ts

Results

Measured on Apple M-series, PostgreSQL 16, local Docker. Your results will vary.

BenchmarkAvgP50P95P99
A) findMany — no extension3.11ms2.43ms5.78ms11.40ms
B) findMany — with soft-delete filter2.01ms1.61ms4.44ms7.48ms
C) delete — hard delete0.53ms0.52ms0.68ms0.77ms
D) delete — soft delete0.54ms0.53ms0.69ms0.77ms
E) cascade (User + 3 Posts + 6 Comments)0.56ms0.56ms0.72ms0.76ms

findMany filter overhead: -1.10ms (-35%) — faster with soft-delete filterSoft vs hard delete: 0.54ms vs 0.53ms — identical

Interpretation

Read filtering makes queries faster. The WHERE deleted_at IS NULL condition reduces the result set (250 live rows vs 500 total), cutting query time by 35%. This is not overhead — it's a net performance gain.

Soft delete vs hard delete is identical at ~0.53ms. The extension converts DELETE to UPDATE SET deleted_at = now(), which PostgreSQL executes at the same speed as a real delete for single-row operations.

Cascade is surprisingly fast at 0.56ms for a User with 3 Posts and 6 Comments. The extension batches the cascade UPDATEs efficiently. Performance scales linearly with the number of related records, but even with 9 related rows the cost is sub-millisecond.

Methodology

  • performance.now() for millisecond-precision timing
  • Raw SQL table creation (no Prisma migration) — matches e2e test pattern
  • Cascade benchmark uses createPrismaSoftDeleteExtension with explicit cascade config
  • Each delete benchmark seeds fresh rows to avoid measuring empty-set operations

Released under the MIT License.