Your data moves overnight.
OpenForce doesn't hand you a spreadsheet and a weekend. It reads your org through Salesforce's own Bulk API, works out the correct load order from your object relationships, loads every record into your new PostgreSQL database in one idempotent pass, and produces a signed validation report before anyone flips a switch.
"Overnight" is measured, not promised — it falls out of your record counts, the Bulk API's own limits, and how much parallelism your org allows. Below is the arithmetic, the honest edges, and the report an operator and a separate verifier both sign.
Your Salesforce data, moved overnight — five things happen, in this order, every run.
- 01Automated extract. Bulk API 2.0 query jobs pull every object — including recycled and archived rows — under your own credentials. Your record Ids, created dates, and owners carry across verbatim: the 18-character Salesforce Id becomes the PostgreSQL text primary key.
- 02Ordered automatically. Parents load before children. The load order is computed from your object graph by a topological sort; circular and self-referencing relationships are detected and resolved with a second-pass update — no hand-built runbook of "load Accounts, then Contacts, then…".
- 03Resumable. Re-run it. Every row upserts by its Salesforce Id (
INSERT … ON CONFLICT DO UPDATE), so a repeated or delta pull updates in place instead of erroring — the basis for a tight cutover with a short write-freeze. - 04Checksummed. Every table gets a row-count, a foreign-key integrity scan, and a per-value content checksum. A single altered cell is detectable.
- 05Signed off. A PASS / FAIL / INCOMPLETE validation report — nothing estimated, nothing fabricated, blanks shown as blanks — that an operator and a separate verifier both sign before go-live.
Feasibility is not a vibe — it's records × the Bulk API's published limits × parallelism. Records to roughly 50 million rows sit under every Bulk API 2.0 daily cap. Files are the real overnight risk, and we say so out loud.
Records — well under every cap (CSV assumed ~2 KB/record)
| Org size | CSV volume | Result pages @1 GB | vs the 1 TB/day results cap |
|---|---|---|---|
| 1 M records | ~2 GB | ~2 | 0.2% |
| 10 M records | ~20 GB | ~20 | 2% |
| 50 M records | ~100 GB | ~100 | 10% |
Bulk API 2.0 daily caps: 150 M records ingested, 1 TB query results, 10,000 query jobs per rolling 24 h, up to 25 concurrent jobs so objects extract in parallel. The binding constraints on records are the per-object 20-minute query timeout and, for a single very large object, a 15 GB single-job retrieval ceiling — split by date/Id range past that. A single object above ~10 M rows is rehearsed against a sandbox before any overnight window is quoted.
Files — one API call each, against your daily entitlement (the honest edge)
| Files in org | REST GETs needed | On an EE org with 150,000 requests/day |
|---|---|---|
| 50,000 | 50,000 | Fits in one day — about 33% of the pool. |
| 500,000 | 500,000 | Exceeds a single day — a multi-night run or a purchased-calls bump. |
| 5,000,000 | 5,000,000 | Multi-week at that entitlement — you raise the entitlement first. |
Each attachment is one authenticated REST GET against your standard API allocation — it cannot be batched, one call per file. So "overnight" is realistic for records at typical mid-market sizes, and for files only when the file count fits your org's REST entitlement — otherwise files are a multi-night pass or a temporary entitlement increase. (An Enterprise org with 50 user licenses gets 100,000 + 50 × 1,000 = 150,000 requests/24 h.)
The alternative is a person driving Salesforce Data Loader by hand: partitioning CSVs, fighting encodings and timezones, building VLOOKUP sheets to re-parent children, and reconciling error files with no way to resume. Every ritual on the left becomes a pipeline step on the right.
| Their ritual — Data Loader, by hand | Our pipeline step |
|---|---|
| Hand-partition CSVs under a ~5 M-row ceiling | Bulk API 2.0 jobs, paginated automatically |
| Re-encode every CSV to UTF-8 to dodge BOM/Excel errors | Typed load straight from the API, no spreadsheet |
| Dates shift by your machine's timezone | UTC carried into timestamptz — no drift |
| Build External-Id + VLOOKUP sheets to re-parent children | Relationships ordered from your schema automatically |
UNABLE_TO_LOCK_ROW: toggle Serial Mode, guess a batch size | Loads in dependency order into your own database |
| Reconcile success/error files by hand; no resume | Idempotent upsert — safe to re-run |
| No checksums, no verification, no report | Row-count + FK + content checksums + a signed report |
| Load order is a human runbook | Load order computed from your object graph |
Nothing is estimated and nothing is fabricated. Missing evidence renders as not provided
and forces INCOMPLETE — never a fabricated zero. Here is precisely what the verdict checks
today, said without inflation.
What each table is checked for. A row-count reconcile, a foreign-key orphan scan that must
come back 0, a primary-key-set checksum, a per-value content checksum, and a blank-date guard
so an empty Salesforce date loads as NULL, never a fabricated now().
The verdict states. PASS, FAIL (on any orphan or count mismatch), or INCOMPLETE (on any missing count or checksum). Two people sign: the operator runs it, and a separate verifier signs — one person cannot be both.
What the checksums do — stated honestly. Per-column checksums are recorded on both the Salesforce and PostgreSQL sides and compared during the sign-off review; the run is not marked complete until counts, foreign keys, and checksums are reconciled. We deliberately do not claim the run "auto-fails on a checksum mismatch" — automatic source-vs-loaded comparison inside the verdict is on our build list, so today that reconciliation is a step in the human review, not an automated gate. We would rather under-claim here than overstate what the machine does.
- AAPI entitlement. Your Salesforce edition's daily API allocation sets how fast files move; a very file-heavy org may need a few nights or a temporary allocation bump.
- BA short write-freeze window. For the final delta and cutover — typically a planned evening, not days. The delta pull watermarks on
SystemModstamp, so it catches every save. - CA sandbox rehearsal. We do a full dry run against a sandbox first, so cutover night has no surprises.
- DVerification. Per-value checksums are recorded on both the Salesforce and PostgreSQL sides and compared during the sign-off review; the run is not marked complete until counts, foreign keys, and checksums are reconciled.
Honest scope: user identity is preserved as the source Salesforce Id, and mapping those to new app users is a defined follow-up step; Activities (Tasks/Events) and compound Address/Geolocation and multi-currency columns are known, tracked fidelity items — we flag what a run does not yet carry rather than imply 100% object coverage. No time estimates beyond the arithmetic above.