Feature design: bounded native ClickHouse to MSSQL transport¶
- Status: IMPLEMENTED
- Maintainer approval: explicit
APPROVEDin the implementation task, 2026-09-10 - Owner: dpone maintainers
- Target release: unassigned
- Last verified: 2026-09-10
This specification is for framework maintainers and connector authors. It defines an opt-in replacement for the complete character spool on bounded ClickHouse to MSSQL loads. Implementation and deployment prerequisites are tracked in the native transport guide; live certification remains unverified. See the current route guide for usable behavior and the feature design standard for approval.
Independent architecture review resolved the initial admission boundary to one ClickHouse query snapshot and resumable completed staging. Interrupted partial extraction requires complete re-extraction. This deliberately does not promise a reusable ClickHouse snapshot across processes. Track runtime patch removal separately in the removal inventory.
Executive summary and existing execution path¶
The audited base is v0.74.36, commit
3977ca2d04ca5dbcf3338d7c31faff8a1199549e. The following are code observations,
not measured performance results:
StreamingRowsArtifact.materializecalls the optional staging managerinsert_streaming_rowsmethod.MSSQLStagingManager.insert_streaming_rowscallsinsert_rows, which callsMssqlCharacterSpool.import_rows. The complete stream is serialized before import. Ordinary raw staging uses character types before native projection.NativeTransferCapabilityPlannerhas a typed row-stream specialization for MSSQL to ClickHouse. ClickHouse source and MSSQL sink returnclickhouse_stream_export_not_declaredandmssql_stream_staging_not_declared; RowBinary is a ClickHouse ingest format.- Independently,
supports_staged_loadrequiresstage_payload,finalize_staged_load, andabort_staged_load.MSSQLSinkhas none of those methods.PayloadLoadertherefore reportsstaged_load_port_unavailableand chooseslegacy_post_finalize. This warning does not mean the MSSQL transaction finalizer itself is non-atomic. - The existing
mssql_typed_bcpcodec emits length-prefixed SQLCHAR UTF-8, including text representations of numbers and temporal values. It is not a native binary value encoder and must not be presented as one.
The approved throughput design deliberately specifies one complete immutable character spool and one BCP process. The bounded streaming implementation supports MSSQL to ClickHouse typed transport and PostgreSQL to ClickHouse windows. ADR 0057 initially admits a local ClickHouse Atomic target. None of these establishes the requested MSSQL capability.
Personas and customer journey¶
| Persona | Goal | Success signal |
|---|---|---|
| Data engineer | Load a wide, complete interval efficiently | Explicit native route and resource limits in the plan |
| Operator | Recover interrupted staging safely | Verified chunk reuse or an explicit re-extraction requirement |
| Data owner | Preserve exact rows and target visibility | Duplicate parity, outside-window invariance, one publication |
| Connector author | Reuse bounded execution | Narrow source, transport, staging and publication contracts |
Journey: discover the route; verify source-snapshot and target-transaction prerequisites; configure the opt-in policy; inspect admission before extraction; run synthetic correctness checks; inspect observed phase evidence; recover using the original invocation; promote only an exact revision with environment-specific evidence. Existing manifests retain their existing behavior.
Scope and non-goals¶
Include bounded concurrent encoding/import, native typed chunks, isolated staging attempts, prepublication quality, durable recovery and one transactional interval replacement. Full refresh is a second complete-boundary variation of the same transport; it has no interval predicate and retains the existing catalog policy.
Exclude CDC and cursor inference, arbitrary independent source snapshots, deduplication, append/merge route enablement, automatic target index removal, new database drivers, import-time patches, deployment changes and publication of a package. A partition loop must not publish individual target intervals.
Public contract and compatibility¶
CLI and Python API¶
Keep existing commands, exit codes and defaults. An explicit unsupported native route fails during planning/admission rather than silently selecting character transport. The composition root supplies a single-query source adapter and independent worker connector factories; credentials remain outside the manifest.
Expose staged loading through the existing three-method port. Stage returns an
owned prepared handle with verified row authority; finalize accepts that same
handle and invokes one transaction finalizer; abort may remove only unpublished,
settled attempts belonging to the handle. Unknown publication cannot be aborted.
The combined load entry point delegates through the same lifecycle so there
are no competing implementations. Unsupported strategies retain their explicit
compatibility behavior; method presence alone must not admit them. Add optional
supports_staged_load_for(load_config) admission to existing detection. Sinks
without it retain their current three-method detection. MSSQL admits the new
native path only for full refresh and explicit interval partition replacement;
its legacy configurations retain existing governance selection.
Manifest and schema proposal¶
Reuse source.options.native_transfer.wire and
source.options.native_transfer.execution. Add binary_format: mssql_native
to the existing wire vocabulary, with route/type admission. Add
execution.chunking.mode: bounded_stream, reusing parallelism and
checkpointing: resumable. Unlike bounded_window, it partitions one acquired
stream into physical chunks and does not open independent interval queries.
Full refresh has no window block; partition replacement requires an explicit
fixed sink.strategy.window. Preserve existing bounded_window composition
and behavior for other routes. Introduce no top-level transport registry.
Freeze physical limits in a new optional execution.native_chunks object:
| Field | Default | Validation and meaning |
|---|---|---|
max_rows |
65536 | Integer 1–1000000, excluding booleans; rows per physical chunk |
max_bytes |
16777216 | Integer 1–1073741824; encoded bytes including framing |
max_row_bytes |
1048576 | Positive integer no greater than max_bytes |
max_pending |
2 | Integer 1–64; waiting chunks, excluding active workers |
max_total_encoded_bytes |
required | Positive hard cumulative payload limit |
max_staging_tables |
1024 | Positive cap on simultaneously owned stage objects |
stage_allocated_bytes_stop_threshold |
required | Observed allocation threshold; not an exclusive reservation |
parallelism retains its 1–64 bound. Enforce aggregate spool admission for
(parallelism + max_pending + 1) * max_bytes plus known format/receipt overhead.
Check SQL allocation before scheduling imports and after their settlement. A
crossed allocation threshold stops new work and prevents publication; active
imports may overshoot it, and evidence records that overshoot. Count all attempt
and prepared stages. Observe database/log headroom and required permissions
before extraction and publication. These are capacity checks, not reservations
against unrelated writers. Strict physical storage caps require separately
provisioned database/storage limits. Driver allocation and RSS are observed
separately and are not guaranteed by encoded-byte limits.
These proposed fields are not accepted by v0.74.36. Update the schema producer, parser, plan diagnostics and reference together after approval. Keep the current one-spool regression test and the current route example as compatibility tests.
Artifacts and migration¶
Define dpone.mssql_native_chunks.v1 receipts binding invocation, source query,
window, schema, wire profile, logical chunk, physical ordinal and owned attempt.
Bind immutable chunk bytes before import, then vendor count, rejects, independent
stage count and typed multiset digest. Record source EOF separately from chunk
completion. Chunk identity never deduplicates business rows. Query identity binds
the extraction and schema but is not a durable source snapshot token.
Keep identity and content evidence deterministic by canonical ordering of logical chunk and physical ordinal, independently of worker completion. Timings are observations and do not enter identity. Existing artifact schemas are unchanged. Migration is explicit opt-in after acceptance; rollback restores the old manifest and package only after unresolved publication has been reconciled.
Detailed algorithm¶
- Resolve the explicit UTC half-open interval once. Validate complete coverage, source types, target catalog, resource reservations and transaction authority before row I/O. Empty intervals and unsupported capabilities fail explicitly.
- Acquire fenced invocation ownership. Admit one directly connected local plain MergeTree source table in an Atomic database, with ordinary explicit columns. Execute exactly one data-bearing SELECT for the full table or frozen interval. Reject views, remote/Distributed tables, custom queries, joins, FINAL, sampling, LIMIT/OFFSET, semantic MergeTree variants, unsupported temporal representations and source policies that cannot be proven compatible. The query owns its snapshot only until EOF; business changes spanning separate mutations are not claimed to be an atomic source transaction.
- Persist the plan using existing durable CAS journal contracts. Derive logical physical chunk ordinals from this invocation's observed row order, never LIMIT/OFFSET over a reopened query.
- One producer owns the source iterator. Pull typed frames under backpressure
and validate range, arity, nullability and value bounds. Explicitly spawned
processes encode native files; never fork inherited connections. Bound IPC
frame bytes independently, with the same configured
max_bytesceiling. Import workers own independent target connections and attempt identities. The parent owns scheduling, source lifetime, fencing and journal writes. Never write a full-stream character intermediate. - Seal each bounded file, persist its receipt, and BCP it into its own typed staging attempt. File imports may overlap encoding of later chunks. Do not share mutable connection state, reject paths or count-delta authority.
- Compare vendor count, reject status, server count and typed content evidence.
Persist verified attempt receipts with fencing before releasing local files.
Persist
stage_completewith atomic CAS only after source EOF, contiguous ordinal coverage and verification of every attempt. Bind total rows, schema, content evidence and ordered receipt digests. Missing or duplicate ordinals fail. - Prepare a typed replacement stage using
UNION ALLsemantics and the existing native lineage authority. Reject schema drift and authored incompatible quality policy. Run source/staging quality before any target publication. - Enter the existing MSSQL serializable transaction and target fence. Revalidate catalog and receipt identity. For a bounded interval, delete exactly the frozen predicate and insert verified replacement rows; an empty source still deletes that interval. Preserve NULL-window rows and all outside-window rows. Full refresh uses its existing transaction-safe strategy and catalog checks.
- Write the authoritative target receipt in that same transaction and commit once. Never use staging-discovered values as authority for an empty interval. Target readers under supported isolation observe a committed old or new result; dirty reads are outside this guarantee.
- Reconcile a lost commit acknowledgement through the target receipt before any retry. Persist required evidence, then advance source state with CAS/fencing. Cleanup runs only after publication status and owned-resource settlement are known. A postcommit evidence failure requires completion of that publication.
admit -> fence -> single query snapshot -> durable plan
-> bounded native chunks -> isolated verified attempts -> stage_complete
-> prepublication quality -> one transaction and receipt
-> reconcile commit -> durable evidence -> source state
State, retries and cancellation¶
Use planned, staging, verified, publishing, published, evidence-complete and succeeded states with failed/outcome-unknown branches. Freeze their serialized representation before implementation. A physical attempt may be reused only after server identity, rows, digest and invocation binding are reverified.
Allow at most two transient import retries per physical chunk, persisted durably. Fence and join an old writer before replacing an unverified attempt. Contract, schema, authentication, quality and capacity failures are terminal. On cancel, stop pulling, close source iterators, cancel/join workers and retain unresolved attempt metadata. Cleanup failure does not replace the original error.
Before stage_complete, a process restart fences/settles all attempts, proves no
publication began, and discards all extraction stages before starting a new
invocation. Return reextract_required; retain the old audit record. Never resume
a mutable source at a row offset. During a running query, retry an import only
from its retained immutable file; never reopen the source to reconstruct a prefix.
After stage_complete, reverify stages and resume publication without a source
connection. Missing or altered stages fail closed. After publication intent,
inspect the target receipt first. Published recovery only completes evidence/state.
Type and edge-case contract¶
Share the finite native framing authority with native_wire_mssql_framing and
the existing MSSQL native decoder; add a separate checked encoder responsibility.
Generate vendor format metadata from exact native target types. Do not infer a
wire representation from generic Python str conversion.
Test integer boundaries including UInt64, Decimal precision/scale, binary bytes,
Unicode, UUID order, dates and temporal ticks. NULL, empty text and literal NULL
markers remain distinct. Reject unsupported nested types, NaN/infinity profiles,
overflow and fractional truncation. DateTime64 precision above Python datetime
precision requires a lossless source representation or rejection before extraction;
mapping to datetime2(7) alone cannot authorize discarded fractional digits.
Duplicate rows, including duplicates spanning workers, retain multiplicity. Source NULL-window values lie outside the selected interval. A valid nonzero interval containing zero source rows replaces that interval with an empty set; an explicit incompatible min-rows quality gate rejects before publication.
Proposed synthetic migration fragment¶
This is a design-review fragment to merge into a future valid manifest. It is not executable on v0.74.36. It deliberately contains no connection binding; the composed application supplies connection factories and source authority. The schema and a complete tested composition example must land before migration.
source:
type: clickhouse
options:
native_transfer:
wire:
mode: typed_binary
binary_format: mssql_native
execution:
chunking:
mode: bounded_stream
parallelism: 4
checkpointing: resumable
native_chunks:
max_rows: 65536
max_bytes: 16777216
max_row_bytes: 1048576
max_pending: 2
max_total_encoded_bytes: 1073741824
max_staging_tables: 1024
stage_allocated_bytes_stop_threshold: 4294967296
sink:
type: mssql
strategy:
mode: partition_replace
atomicity: target_atomic
window:
column: observed_at
anchor: data_interval_end
lookback: P7D
timezone: UTC
Use a fixed aware interval end in execution context. Do not silently replace it with wall time or a target-derived cursor. Future plan output must show actual admission and the selected transport, not merely echo this request.
Architecture and ADR extension¶
| Component | Responsibility |
|---|---|
| Existing bounded executor/journal | Logical ownership, retry budget, durable ordering |
| ClickHouse single-query adapter | One acquired query snapshot and producer lifecycle |
| Native MSSQL encoder | Pure typed value/framing validation |
| Bounded chunk transport service | Backpressure, immutable files, worker lifecycle |
| MSSQL staging service | Independent typed attempts, parity and prepared handle |
| MSSQL staged-load adapter | Existing governance port and transaction finalizer delegation |
| Evidence producer | Stable receipts and observed phase/resource measurements |
Contracts belong in dpone.contracts, narrow injected ports in dpone.ports,
execution policy in dpone.runtime, and vendor I/O in adapters. Reuse existing
factories; no new generic registry. Native framing and staging are separate
variation points, allowing a second typed source with its own snapshot authority.
The existing bounded-window journal supplies CAS/fencing conventions; do not
pretend its independently reread logical-window algorithm matches a single stream.
Extend ADR 0057 with transactional MSSQL publication and target-receipt recovery.
Do not transplant ClickHouse UUID/EXCHANGE recovery. Target transaction locks
must protect the predicate against concurrent DML through commit; DDL/catalog
checks remain under the existing authority. Journal leases alone are insufficient.
Retain existing hard limits from docs/benchmarks/quality_budgets.yml and split
modules by responsibility, without threshold exemptions.
Alternatives and current primary-source comparison¶
Sources checked 2026-09-10. Adoption choices are design judgments; no comparative speed claim has been measured.
| System or option | Observation and decision |
|---|---|
| SQL Server native BCP | Native files carry typed values with format constraints. Adopt bounded sealed files and explicit Unicode/type profiles; validate actual driver/server combinations. Microsoft native-format guide |
| SQL Server bulk loading | Parallel heap imports have specific index/locking prerequisites. Use independent attempts rather than assuming concurrent count deltas on one heap are attributable. Microsoft BULK INSERT reference |
| dlt | Worker counts, buffered items and file size limits control load work. Adopt explicit bounds, while retaining dpone's separate atomic publication contract. dlt performance |
| ClickHouse | A running SELECT holds its snapshot's data parts until query completion. Adopt one query; do not infer durable cross-process snapshot reuse. ClickHouse 25.6 explanation |
| ODBC/OLE DB bulk API | Microsoft documents memory-buffer bulk interfaces. Defer direct buffer transport because it adds binding/lifetime and receipt proof beyond the existing BCP adapter. Microsoft preparation guide |
| Length-prefixed SQLCHAR | Existing local codec avoids delimiters but still serializes characters. Reject as evidence of a native binary fast path. |
| FIFO into BCP | Defer: it lacks the current sealed-file proof before import. |
| Informatica, Airbyte, Fivetran, Pentaho | N/A for this adapter-format decision; no new connector protocol or product-engine comparison is made. |
| SSIS | SQL Server bulk primitives above are relevant; SSIS orchestration itself is N/A. |
| gusty, Astronomer Cosmos, Apache Beam | N/A: scheduler/DAG/distributed execution changes are outside this bounded adapter change. |
Evidence and measurable differentiation¶
Producer output includes exact commit, dependency/server versions, sanitized route identity, rows, encoded bytes, physical chunks, attempts, BCP processes, configured workers, observed peak active workers, per-phase active-worker time and wall time. Effective parallelism for a phase is worker-active seconds divided by that phase's wall span; do not substitute requested workers. Empty phases record zero work and an explicit unavailable ratio. Separate encode/spool, transport, stage verification and publish; overlapping durations must not be summed into elapsed time.
Correctness uses exact typed multiset equality in hermetic and small live cases; scalable digests record their version and probabilistic limitation. Benchmark the audited base against the candidate on the same synthetic narrow/wide/NULL/skewed datasets and resource limits, one warmup plus at least three trials. Record median and dispersion, RSS and local/server storage high-water marks. Initial acceptance requires zero fidelity failures, zero partial publications, respected resource limits and demonstrated worker overlap. Numeric speed claims require those runs; no production-ready or acceleration claim follows from mocks.
Test, documentation and rollout plan¶
Use red-green-refactor for new admission, bounds, native framing, concurrent attempts, staging governance and recovery contracts. Preserve the existing one-spool/one-BCP default test. Use barrier-controlled hermetic workers to prove overlap without flaky time thresholds. Inject failures before/after import ACK, checkpoint persistence, publication ACK, evidence and state. Test tampered stages, missing ordinals, pre-EOF re-extraction, source-free post-EOF recovery, cancellation and exhausted retries.
An approved disposable ClickHouse/MSSQL environment must move real synthetic rows, verify duplicate/type parity, late changes/deletes, empty windows, outside-window invariance, retry and target catalog preservation. Missing live infrastructure is SKIP/UNVERIFIED. Exact-commit evidence is required before route certification.
Run focused tests, select_checks.py, ruff, formatting, mypy, import/layer/module
gates, full non-live pytest, docs contracts and strict MkDocs. Keep observed baseline
failures separate from regressions. Update the route tutorial, migration example,
schema reference, performance runbook, ADR and changelog when behavior lands.
The root agent is the sole integrator and shared-file writer. Explorer, architect, test/certification and docs/UX agents are read-only. A fresh-context reviewer checks the final implementation before integration. No merge or release is authorized.
The follow-up data-delivery acceleration specification preserves this route contract while defining bounded preparation/producer optimizations and independent implementation tasks.
Approval checklist¶
- Actual route and missing capabilities traced against the named base.
- Resolve source lifetime, strategy admission, resource semantics and worker model.
- Compatibility, public options, evidence and migration impact identified.
- Primary-source alternatives and reproducible validation plan recorded.
- Single integrator and independent read-only reviews assigned.
- Maintainer marks this concrete extension APPROVED before production edits.
Implementation refinements¶
Source admission requires an injected exclusion authority covering relation and policy DDL from catalog inspection through source cleanup. The nonzero Atomic relation UUID is persisted with completed payload metadata. DateTime values use integer microsecond SQL projections and exact UTC window strings, avoiding native driver float decoding and parameter truncation. Overflow modes throw and query cache reuse is disabled.
The durable journal stores chunk states and a nested publication state. Top-level
stage_complete binds contiguous chunk receipts and completed source metadata in
one CAS. The nested preparation/publication states distinguish source-free rebuild,
unknown commit, evidence completion and source-state completion. Source progress
never advances on an individual chunk receipt.
Native wire limits apply to business bytes. Verification of generated lineage uses a separately derived finite schema bound and preserves physical nullability. The observed SQL allocation stop threshold counts reserved pages of native staging tables; volume and transaction-log headroom are separate admission observations. Mandatory deployment callbacks must use real fencing, capacity and persistence services. Synthetic no-op authorities are not a deployment configuration.
Implementation evidence (2026-09-10)¶
Frozen implementation commit: a915444105f4ed6fb71d0e35e796a9233a43751b.
The subsequent status/evidence update changes documentation only.
- PASS: complete non-live Python 3.12.11 suite: 20,595 passed, 570 skipped,
549.67 seconds. Used
-n 2 --dist loadfileto avoid host oversubscription; the selected test suite is unchanged. Skipped cases are not passes. - PASS: Ruff, formatting, configured mypy and explicit mypy over 43 changed modules; import, layer, class-responsibility and exact-commit module-size gates. Existing architectural budgets and debt baselines were not relaxed.
- PASS: focused native source, encoder, real spawned execution, existing transaction-finalizer, capacity, journal/recovery and compatibility tests. Reviews reproduced and corrected temporal precision, Unicode sizing, lineage overhead, ownership, exact-capacity and failure-observation issues.
- PASS: documentation language/link checks, strict MkDocs and generated references; four package builds, twine and all eight archive inspections; fresh isolated-wheel native imports.
- PASS: CI for the frozen implementation. This result applies to the named implementation commit, not subsequent integration.
- SKIP / UNVERIFIED: live ClickHouse/MSSQL interoperability, deployed DDL/writer authorities and performance trials. No approved native live environment was supplied. Implementation status is not route certification or release approval.
Local command output is retained under
test_artifacts/bounded-mssql-typed-transport/, including
native-full-suite-final.log, native-module-size-final.log,
native-architecture-overshoot-final.log, native-mypy-changed-final.log,
native-archive-gate-final.log and native-wheel-smoke-final.log.
The review PR carries current integration
checks and owner attestations. This evidence does not authorize tagging or publication.
Integration with 0.75.0¶
The maintainer approved integration while preserving the parallel 0.75.0 release.
The release tag and source base are 67c8ade61732fb1bff267ce3bd26f2af776e79e6.
All 91 upstream files outside the composed changelog, navigation and generated
metrics remain byte-identical to that source. Package versions and dependency
lock remain those of 0.75.0.
Combining the branches exceeded the runtime-to-contract dependency budget. Pure transaction identity policy now resides in the canonical contracts package; the runtime retains source resolution and injects route/backfill providers. Public signatures, validation order, errors and identity hashes are preserved. The focused compatibility suite passed 278 tests. Architectural budgets and baselines remain unchanged. Current combined-commit validation is recorded in the PR; previous implementation evidence above is retained with its exact scope.
Combined implementation commit: 088959fb6f2abf095de691049d37c0b3813c4280.
The subsequent update regenerates tracked-file metrics and records evidence only.
The complete non-live suite passed: 20,794 passed, 570 skipped, 438.11 seconds
(-n 2 --dist loadfile). Ruff, formatting, configured and focused mypy, import,
layer, exact-commit module-size and 41 architecture fitness tests passed.
Documentation checks, strict MkDocs, all four 0.75.0 package builds, eight archive
inspections, twine and isolated-wheel native composition imports passed.
Native live certification remains SKIP for the reason recorded above.