Every BES (Bireysel Emeklilik Sistemi) team I have worked with eventually hits the same wall, usually around the third or fourth EGM submission cycle. Someone in compliance says "we just need a report on Friday's position." Someone in engineering builds a query. It works for two months. Then a contribution posts late, a fund revaluation lands mid-extract, or a participant transfer executes between two joins in the same pipeline — and suddenly the numbers reconciled against the custodian differ from the numbers submitted to the regulator by 47 kuruş across 180,000 accounts.
That 47 kuruş is not a rounding bug. It is a consistency boundary violation. And it is the actual engineering problem of regulatory reporting in pension systems.
The Illusion That Submission Windows Are a Reporting Task
The standard framing goes like this: the regulator wants a snapshot as of T. You extract at T+1 morning. You submit at T+2. Done.
This framing works on paper because it assumes the underlying system holds still. In production, none of the following stop during your submission window:
- Contribution files from employers continue to arrive and post backdated entries
- Fund NAVs get corrected by the portföy yönetim şirketi two or three days after the valuation date
- Participant lifecycle events (entry, exit, fund switch, vesting) keep being written
- Reversal transactions from the previous week's clearing errors post against T-dated positions
- State transfer flows (aktarım) execute with legally binding effective dates that predate your extract
The pipeline is not reporting on a static system. It is reporting on a system that is actively rewriting the past while you try to describe it.
What the Freeze Actually Looks Like
In most Turkish pension operations I have seen, the "freeze" is not a technical mechanism. It is a WhatsApp message that says "nobody run anything until Onur finishes the EGM extract." This is not a joke — it is the actual control.
The reason it exists is that the extract logic almost always spans multiple queries: one for participant master, one for fund positions, one for contribution ledger, one for state contribution matching, one for lifecycle events. These queries do not share a transaction. If a fund switch commits between query 2 and query 3, the participant appears in fund A in the position table and in fund B in the event table. Reconciliation fails silently because both numbers look plausible.
The operational freeze is a human patch over the absence of a snapshot isolation strategy.
Why You Cannot Just Use a Read Replica
The common suggestion — point the extract at a read replica or a data warehouse — solves the wrong problem. It gives you a stable read surface but not a legally coherent one. Consider:
- The replica is behind by 15 minutes. A contribution that legally counts for T because it arrived at 23:58 may not be on the replica when your job starts at 00:15.
- Late-arriving corrections from the custodian are pushed to the OLTP system with backdated valid_from timestamps. Your replica sees them as new rows; your regulatory logic must treat them as amendments to a prior state.
- The warehouse ETL itself is a pipeline with its own consistency window. You are now defending two boundaries instead of one.
A replica gives you read stability. Regulatory submission requires temporal correctness — a very different property.
The Real Design: Bitemporal Snapshots With Explicit Cutoffs
The only architecture I have seen survive audit cleanly is one that treats every regulatory-relevant table as bitemporal: it stores both the business effective date and the system transaction date. The submission pipeline then does two things the naive version does not:
- Fixes a system time cutoff at the start of the extract and uses it as a constant across every query in the run
- Applies business date filters separately, so a correction posted at system_time = T+3 with effective_date = T-2 is either included or excluded based on the regulator's rule, not based on when the ETL happened to see it
Concretely, every extract query gets a WHERE system_recorded_at <= :cutoff_ts predicate, and :cutoff_ts is captured once, at the start of the job, and passed to every downstream step including reconciliation. This is the substitute for the freeze. Production keeps writing. Your report does not see anything written after the cutoff.
The Amendment Problem Nobody Budgets For
Even with bitemporal snapshots, you still owe the regulator an answer for what happens when a correction lands after submission. If a portföy yönetim şirketi reissues a NAV for the reporting date three days after you submitted, the report you filed is now wrong by construction.
The operational answer, in practice:
- Every submission carries a version number and the cutoff timestamp it was built against
- Amendments are computed as deltas against the last submitted snapshot, not as fresh extracts
- The audit trail must reconstruct exactly what was known at submission time — which is why the system_time cutoff is not just an ETL trick, it is legal evidence
Teams that skip this end up either refusing to correct errors (which the regulator eventually notices) or overwriting history (which the regulator notices faster).
What To Actually Build
If you are staring at a BES pipeline that currently relies on the WhatsApp freeze, the migration path is not glamorous but it is finite:
- Add
system_recorded_atandeffective_from/effective_tocolumns to every table the regulatory extract touches. Backfill from transaction logs where possible. - Rewrite the extract as a single job that captures one cutoff timestamp and passes it to every query, including all downstream reconciliations and validations.
- Store the submitted snapshot as an immutable artifact — parquet on object storage is fine — keyed by submission version and cutoff.
- Build the amendment path as a diff against the stored snapshot, not as a re-extract.
- Remove the operational freeze last, only after three or four cycles have shown the snapshot logic reconciles to the kuruş.
The reason to do this is not elegance. It is that the freeze eventually breaks — someone runs a job, an employer file arrives late, a fund revaluation is not deferred — and the reconciliation gap becomes a regulatory finding. The engineering problem was never the report. It was defending a consistency boundary against a production system that does not know the boundary exists.