database-bulk-loading · git:20260906.e6ad4bd · 2026-09-06 · sha256 f2af1606dd21105d

database-bulk-loading git:20260906.e6ad4bdA

Immutable. This exact content never changes and is served at /api/v1/blob/f2af1606dd21105d.

---
name: database-bulk-loading
description: >
  Designing and diagnosing high-volume database ingestion from the JVM across PostgreSQL,
  MySQL, and SQL Server: JDBC batching and statement rewrite, native COPY/LOAD DATA/Bulk Copy,
  staging, transaction and partial-error semantics, idempotent restart, upsert races, logging,
  parallelism, and post-load validation. Use when a backfill, import, migration, or batch window
  is too slow or unsafe. Not routine ORM fetch/write tuning, which belongs to
  orm-fetch-and-batching-performance.
---

# Database Bulk Loading

## Purpose

Choose the least costly ingestion mechanism that still preserves the required validation,
atomicity, recoverability, and online workload. “More threads” and “larger batch” are not goals;
the job is done when useful rows per second rise without violating correctness or guardrails.

## Inputs required

```text
engine, server/driver versions, topology, durability and replication mode:
source format/location/trust, rows and bytes, row width, and error distribution:
target constraints, triggers, indexes, generated keys, and online traffic:
required atomicity, duplicate/upsert semantics, rejection policy, and restart point:
current mechanism, batch size, transaction size, throughput, CPU/I/O/log/network, and heap:
window/SLO, staging/disk/log headroom, privileges, and rollback constraints:
```

For JVM implementation changes, inspect compiler release/toolchains, runtime images, resolved
JDBC driver and ORM versions, connection properties and transaction-manager ownership. This skill
imposes no Java baseline; preserve the project's target and dependencies. Without phase timings
or failure semantics, propose a bounded pilot and mark mechanism/sizing conclusions conditional.

## Workflow

1. Measure rows/s and bytes/s by phase. Attribute time to client materialization, network
   round-trips, statement processing, per-row engine work, log/WAL flush, indexes/constraints, or
   replication. A single total duration cannot select a mechanism.
2. Choose the mechanism level deliberately: individual statements, JDBC batch, driver statement
   rewrite, native bulk API, or server-side set operation from staging.
3. Define transaction and error semantics before tuning. `executeBatch()` is not atomic; verify
   an explicit transaction encompasses all intended writes on transactional storage, including
   native API participation. DDL, sequences and external trigger effects can escape rollback.
   Capture update counts, SQL state/vendor code, warnings, rejected rows, and what
   remains committable after an error.
4. Prefer staging when validation, deduplication, transformation, index suspension, or online
   isolation matters. Load into a table with intentionally minimal structures, validate, then move
   with set-based SQL.
5. Find the batch-size knee under representative data. Network benefit approaches saturation while
   memory, lock duration, retry granularity, statement size, and replication lag keep growing.
6. Pilot parallelism only with a bottleneck hypothesis, spare capacity and independently owned
   key ranges/partitions. It may overlap client waits or use idle server resources; it cannot
   remove a saturated shared log or lock bottleneck. Stop when CPU/I/O/log, lock waits, replica lag,
   or online latency reaches its guardrail.
7. Commit the destination checkpoint with the data wherever they share a transaction resource.
   Acknowledge an external source only after destination commit, with idempotent replay across
   that gap. Reconcile unknown commit outcomes before retrying; see the recovery reference.
8. Finish by checking accepted/rejected/warning counts, constraints, samples or checksums, target
   invariants, statistics, replica convergence, and online SLOs.

## Rules

- Separate three costs: round-trips, work per statement, and work per row. JDBC batching attacks the
  first; rewrite/native APIs attack the first two; only server-side choices reduce index,
  constraint, trigger, logging, and data-work cost.
- If a batch of B rows uses one round-trip instead of B, its round-trip component falls by
  `1 - 1/B`. This is not total elapsed-time improvement: row work, commits and driver behavior
  remain. Measure the knee and vary JDBC batch size separately from transaction/chunk size.
- Never infer batch from an API name or ORM log. Verify server statement/round-trip counts and the
  driver's effective properties.
- Native APIs have different correctness defaults. PostgreSQL `COPY` validates constraints and
  fires triggers; SQL Server Bulk Copy skips some checks/triggers unless enabled; MySQL `LOAD DATA`
  can convert bad input into warnings. Make these choices explicit.
- A durability relaxation needs named data-loss semantics, authority, a timed restoration step, and
  a crash test. A faster import is not evidence that correctness remained intact.
- Dropping indexes on a hot final table can turn the load into an outage and alter constraints.
  Staging is the default location for aggressive optimization.
- Upsert syntax is not portable: conflict target, row-locking behavior, triggers, no-op updates, and
  races differ. Test concurrent writers and avoid rewriting unchanged rows.
- Do not materialize the entire source in the JVM. Stream with bounded buffers and account for
  driver buffering; a fetch/input API that accepts a size does not prove bounded memory.
- Update optimizer statistics after the load and validate the first online plans. Completion before
  statistics refresh can defer the incident until traffic resumes.
- “No exception” is not data quality. Treat warnings and rejected rows as first-class outcomes.

## Output

Produce a load plan with:

```text
chosen mechanism and why the next simpler/faster level was rejected:
transaction, partial-error, warning, retry, and idempotency semantics:
staging/final-table design and index/constraint/trigger handling:
batch/chunk/parallelism values as hypotheses with guardrails:
progress checkpoint and interruption recovery:
pre/post measurements and data-quality assertions:
operational rollback and configuration restoration:
confidence and untested failure modes:
```

## References

- [Mechanisms by engine](references/engine-mechanisms.md) — read before selecting or configuring a
  native API, driver rewrite, or minimal-logging path.
- [Failure, restart, and validation](references/recovery-and-validation.md) — read when partial
  input failure, upsert, restart, cutover, or data-quality guarantees matter.