Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)

Implementation Specs (157)

Detailed engineering and feature implementation specifications.
implementation-specs/specs/KdsCdcIngestion/TASK5.md 24,989 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

TASK 5: Persist Raw CDC Batches And Create Durable Ingestion Job State

Summary

Implement the first durable handoff for KDS CDC after intake validation and tenant resolution: write accepted CDC envelopes to tenant- and entity-scoped S3 raw storage, then create durable internal ETL batch or job state that lets downstream stages resume from raw without depending on SQS retention.

This task freezes raw artifact boundaries, raw payload fidelity, durable stage-batch creation rules, raw-to-batch metadata shape, and SQS acknowledgement gating. It establishes the extract_raw success point for KDS CDC.

This task does not yet implement normalization to load Parquet, Redshift COPY, curated merges, curated export, or final stage checkpoint policy across the full pipeline.

Current Codebase Findings

Repository analysis shows the codebase already has raw S3 storage and stage-batch lifecycle seams, but KDS CDC needs to use them with stricter replay metadata and tenant/entity slice boundaries.

  • backend/Kds.Ai.Warehouse/Models/Storage/RawObjectWriteRequest.cs already supports tenant-scoped raw writes with CompanyId, TenantId, SourceType, BatchId, StreamName, BusinessDate, CapturedAtUtc, FileName, ContentType, and metadata.
  • backend/Kds.Ai.Warehouse/Services/Storage/S3EtlStorage.cs writes raw objects through WriteRawAsync(...) and builds keys from RawObjectKey using the existing ETL S3 bucket and storage rules.
  • backend/Kds.Ai.Warehouse/Services/Storage/EtlS3KeyBuilder.cs already builds raw keys in the required company/tenant/source/date/batch/entity shape: raw/company_id=.../tenant_id=.../source_type=.../{yyyy}/{MM}/{dd}/batch_id=.../entity=.../....
  • backend/Kds.Ai.Data/Models/Ingestion/CreateIngestionBatchRequest.cs, backend/Kds.Ai.Data/Repositories/IIngestionBatchRepository.cs, and backend/Kds.Ai.Data/Services/IngestionBatchLifecycleService.cs already support stage-scoped batch creation, completion, failure, details JSON, upstream linkage, and raw S3 path persistence.
  • backend/Kds.Ai.Data/Entities/IngestionBatch.cs stores CompanyId, TenantId, TenantDatabase, BusinessDate, PipelineStage, DetailsJson, RawS3Path, LoadS3Path, and UpstreamBatchId, which is enough for one raw artifact per stage batch but not for collapsing many tenant/entity raw artifacts into one row.
  • backend/Kds.Ai.Integrations/BrinkPos/BrinkPosExtractJobs.cs is the current reference for extract_raw: start stage batch, write raw artifact, persist RawS3Path, serialize diagnostics into DetailsJson, and mark the batch complete or failed.
  • backend/Kds.Ai.Integrations/Etl/StageDiagnosticsEnvelope.cs and StageDiagnosticsJson.cs already provide the repo pattern for storing structured per-batch diagnostics in DetailsJson.
  • backend/Kds.Ai.Data/Models/Ingestion/UpsertIngestionCheckpointRequest.cs and backend/Kds.Ai.Integrations/Etl/StageExecutionSupport.cs show current checkpoint seams are stage-based and tenant-scoped. They do not yet persist CDC coordinate ranges as first-class checkpoint values, so Task 5 should preserve those ranges in durable batch metadata for Task 8 rather than treating SQS as replay state.

These findings create two important Task 5 constraints:

  1. a KDS CDC extract_raw batch should represent one bounded tenant/entity slice, not one whole mixed poll window, because current IngestionBatch.RawS3Path stores one raw artifact path,
  2. raw-write success alone is not enough; durable batch metadata must also capture coordinate and SQS ordering ranges before messages can be deleted.

Dependencies

  • docs/features/ETL/kds-cdc/index.md
  • docs/features/ETL/kds-cdc/implementation-breakdown.md
  • specs/KdsCdcIngestion/TASK2.md
  • specs/KdsCdcIngestion/TASK4.md

Source References

  • ../../docs/features/ETL/kds-cdc/implementation-breakdown.md#task-checklist
  • ../../docs/features/ETL/kds-cdc/index.md#stage-4-store-to-s3-raw-extract_raw
  • ../../docs/features/ETL/kds-cdc/index.md#stage-5-durable-internal-job-creation
  • ../../docs/features/ETL/kds-cdc/index.md#checkpointing-and-replay
  • ../../docs/features/ETL/kds-cdc/index.md#quarantine-and-failed-packages
  • ../../docs/features/ETL/kds-cdc/index.md#supported-corner-cases
  • ../../docs/features/ETL/kds-cdc/index.md#selected-behavior-summary

Scope From Breakdown

This task covers only the fifth implementation-breakdown item:

  • write accepted CDC envelopes to tenant/entity-scoped S3 raw storage
  • preserve replay-safe source metadata in raw artifacts
  • create durable internal batch/job metadata after raw write succeeds
  • acknowledge SQS messages only after raw persistence and durable resume state exist

Files to Create

1. KDS CDC raw persistence orchestration seam

Create a dedicated seam for taking resolved KDS CDC tenant/entity slices and persisting them as extract_raw artifacts.

Recommended logical location:

  • backend/Kds.Ai.Integrations/KdsCdc/Raw/

Recommended logical types:

  • IKdsCdcRawBatchWriter
  • KdsCdcRawBatchWriter
  • IKdsCdcExtractJobs or IKdsCdcExtractRawJobs
  • KdsCdcExtractJobs or KdsCdcExtractRawJobs
  • KdsCdcRawBatchWriteRequest
  • KdsCdcRawBatchWriteResult
  • KdsCdcRawArtifactDetails

Minimum responsibilities:

  • accept only records that already passed Task 2 validation and Task 4 tenant resolution,
  • split or consume input as one resolved tenant/entity slice per durable extract_raw batch,
  • serialize raw CDC envelopes in replay-safe format,
  • write raw object to ETL S3 storage,
  • create or complete durable stage-batch metadata only after raw write succeeds,
  • return enough information for later SQS acknowledgement and Task 6 normalization handoff.

2. Raw artifact serialization contract

Create a contract for the raw object contents, not only the storage path.

Recommended logical location:

  • backend/Kds.Ai.Integrations/KdsCdc/Raw/Contracts/

Recommended logical types:

  • KdsCdcRawRecord
  • KdsCdcRawBatchPayload
  • KdsCdcRawMetadataRange

Task 5 should explicitly choose the raw payload shape, for example:

  1. NDJSON with one source-faithful envelope per line, or
  2. JSON array or wrapped batch document containing ordered records plus batch metadata.

Preferred direction:

  • use NDJSON or another streaming-friendly JSON form that preserves each original CDC envelope without forcing full-file materialization during replay.

Minimum raw content requirements:

  • original CDC payload,
  • operation code,
  • source coordinates,
  • normalized production ordering token derived from source coordinates when contract profile is production,
  • source table/entity and primary key,
  • resolved company_id and tenant_id,
  • SQS message id, group id, and ordering metadata,
  • durable batch id,
  • worker identity or worker-attempt metadata.

Production ordering token rule:

  • Task 5 must define a durable, comparable ordering token extracted from Debezium/source coordinates for production traffic.
  • This token should preserve enough fields to compare order on replay without reopening SQS state, for example connector identity plus commit/LSN/binlog coordinates plus any row-within-transaction ordering field present in the source envelope.
  • Raw persistence must store both the original source coordinates and the normalized comparable token; later stages may use the normalized token for ordering proof while still retaining the original envelope for replay/debugging.
  • SQS SequenceNumber is transport metadata only for production. It may help diagnose redelivery or FIFO behavior, but it must not be treated as authoritative proof of production CDC order.

3. Durable KDS CDC extract diagnostics contract

Create a KDS CDC-specific details contract for extract_raw batch metadata persisted in DetailsJson and stage outputs.

Recommended logical location:

  • backend/Kds.Ai.Integrations/KdsCdc/Models/

Recommended logical types:

  • KdsCdcExtractBatchDetails
  • KdsCdcRawRangeDetails
  • KdsCdcSqsAcknowledgementDetails

This details payload should be durable enough for:

  • later normalization from raw S3,
  • later checkpoint advancement in Task 8,
  • replay after raw-write success but before later stages succeed,
  • proving which SQS messages became safe to delete.

For production ordering safety, these durable details must also preserve the comparable ordering-token contract chosen above so Task 6 can validate apply order from stored facts rather than from transient worker memory or SQS-only sequence values.

Files to Modify

4. KDS CDC worker handoff from Task 2 and Task 4

Modify likely KDS CDC intake and resolution seams so closed, resolved micro-batches hand off into extract_raw persistence rather than directly to later stages or immediate acknowledgement.

Expected areas:

  • backend/Kds.Ai.ApiService/Jobs/KdsCdc/
  • backend/Kds.Ai.Integrations/KdsCdc/

Required flow:

  • Task 2 worker closes ordered micro-batch,
  • Task 4 resolution splits good records into resolved tenant/entity slices,
  • Task 5 creates durable extract_raw batch per resolved slice,
  • only after raw write plus durable metadata succeed can SQS delete be attempted for those messages.

5. KDS CDC adapter stage wiring from Task 1

Modify or implement the KDS CDC extract_raw stage wrapper introduced conceptually in Task 1.

Expected areas:

  • backend/Kds.Ai.Integrations/KdsCdc/
  • backend/Kds.Ai.Integrations/DependencyInjectionExtensions.cs

Task 5 should define whether the worker:

  1. invokes IEtlPipelineRunner once per resolved tenant/entity slice using the KDS CDC adapter, or
  2. invokes a thinner raw-write orchestration seam that itself produces the extract_raw stage result and then hands downstream work into the pipeline.

Preferred direction:

  • keep KDS CDC aligned with framework-native stage execution and preserve extract_raw as a real stage batch, not an out-of-band worker-only write.

6. Ingestion batch persistence and metadata seams

Review and likely modify:

  • backend/Kds.Ai.Data/Entities/IngestionBatch.cs
  • backend/Kds.Ai.Data/Configurations/IngestionBatchConfiguration.cs
  • backend/Kds.Ai.Data/Repositories/IIngestionBatchRepository.cs
  • backend/Kds.Ai.Data/Repositories/EfIngestionBatchRepository.cs
  • backend/Kds.Ai.Data/Models/Ingestion/CreateIngestionBatchRequest.cs

Task 5 should decide whether current batch storage is sufficient with RawS3Path + DetailsJson, or whether KDS CDC needs an additive child metadata table for richer raw-artifact or acknowledgement ranges.

Strong repo-driven guidance:

  • do not overload one IngestionBatch row to represent many raw artifact paths for many entities,
  • prefer one extract_raw batch per tenant/entity raw object if that keeps current schema sufficient,
  • add a new child metadata table only if required for multiple acknowledgement ranges or artifact references that cannot fit cleanly into structured DetailsJson.

7. S3 storage metadata usage

Review and likely modify KDS CDC raw persistence code to use existing ETL storage cleanly.

Relevant areas:

  • backend/Kds.Ai.Warehouse/Models/Storage/RawObjectWriteRequest.cs
  • backend/Kds.Ai.Warehouse/Services/Storage/S3EtlStorage.cs
  • backend/Kds.Ai.Warehouse/Services/Storage/EtlS3KeyBuilder.cs

Task 5 should specify KDS CDC raw file naming, content type, optional compression, and object metadata fields. Do not invent a separate KDS CDC bucket layout unless later docs change.

8. SQS acknowledgement seam

Modify likely queue orchestration from Task 2 so delete acknowledgement happens only after Task 5 durable boundary is complete.

Expected areas:

  • Task 2 queue abstraction and worker types under backend/Kds.Ai.ApiService/Jobs/KdsCdc/
  • queue client abstraction under backend/Kds.Ai.Integrations/KdsCdc/

Important rule:

  • Task 5 should not allow message delete immediately after successful S3 upload if durable batch metadata creation failed.

9. Tests

Add or modify tests under:

  • backend/tests/Kds.Ai.Integrations.Tests/KdsCdc/
  • backend/tests/Kds.Ai.Data.Tests/
  • backend/tests/Kds.Ai.ApiService.Tests/
  • existing storage tests only if shared ETL S3 behavior needs additive assertions

Required Behavior

Raw storage boundary

extract_raw persists accepted CDC records to S3 raw using the existing ETL company/tenant path layout.

Fixed raw key intent from feature doc:

text
raw/company_id={company_id}/tenant_id={tenant_id}/source_type=kds_cdc/{yyyy}/{MM}/{dd}/batch_id={batch_id}/entity={entity}/...

Task 5 must preserve tenant safety and replay isolation by ensuring each durable raw artifact is scoped to one resolved tenant and one entity/table.

Important consequence from current repo model:

  • one extract_raw batch should correspond to one tenant/entity raw artifact or one bounded artifact set that can still be represented unambiguously by current batch metadata,
  • Task 5 must not mix many entities or tenants into one IngestionBatch.RawS3Path without introducing a deliberate richer metadata model.

Raw payload fidelity

Raw object contents are replay source of truth after acknowledgement. They must preserve the CDC envelope faithfully rather than only storing transformed row DTOs.

Minimum raw contents:

  • original CDC payload,
  • operation code,
  • source table and primary key,
  • Debezium source coordinates,
  • resolved company_id and tenant_id,
  • SQS message id, group id, and sequence metadata,
  • batch id and worker metadata.

Task 5 must explicitly forbid producer-side or extract-stage reshaping into only staging-row fields. Replay after code or mapping fixes depends on original source envelope fidelity.

Business date rule

For KDS CDC raw batches, BusinessDate remains the CDC processing date rule established earlier in the KDS CDC spec:

csharp
DateOnly.FromDateTime(DateTime.UtcNow)

Task 5 must not inherit Brink business-date semantics from source data.

Durable internal job or batch state

After raw S3 succeeds, the system must create durable internal metadata for downstream ETL work before SQS delete.

At minimum, durable metadata must connect:

  • raw batch id,
  • company id,
  • tenant id,
  • tenant database,
  • entity/table,
  • source type kds_cdc,
  • ingestion mode Cdc,
  • pipeline stage extract_raw,
  • raw S3 path,
  • Debezium coordinate range,
  • normalized production ordering-token range,
  • SQS sequence range,
  • status and retry information.

Current repo pattern strongly suggests this metadata can start as structured DetailsJson on a stage-scoped IngestionBatch, provided Task 5 keeps one batch per tenant/entity slice. If richer durability is needed, Task 5 should add an explicit child metadata record rather than storing opaque unqueryable blobs elsewhere.

SQS acknowledgement gate

SQS messages are deleted only after both conditions are true:

  1. raw S3 write succeeds,
  2. durable internal batch or job metadata exists.

Acknowledgement is per message or per receipt handle, not per original poll window.

  • When a poll window splits into multiple tenant/entity slices, only messages belonging to slices that completed raw write plus durable metadata are eligible for delete.
  • Messages from failed slices remain unacknowledged unless they were durably quarantined under an explicit policy.
  • Durable extract details must record or summarize which message ids/receipt handles became safe to acknowledge.
  • Deleting every receipt handle from a mixed poll window after partial success is forbidden.

If raw S3 write fails:

  • do not delete SQS messages,
  • rely on visibility timeout and retry,
  • preserve failure diagnostics.

If raw S3 succeeds but durable metadata creation fails:

  • do not delete SQS messages,
  • allow retry,
  • assume duplicate raw objects may be written on retry,
  • require downstream dedupe to rely on source coordinates plus batch metadata rather than object uniqueness.

If a production message cannot yield a valid comparable ordering token from its Debezium/source coordinates:

  • do not treat FIFO SequenceNumber as fallback ordering proof,
  • fail or quarantine according to the established batch-versus-record safety rule before delete eligibility is granted,
  • do not acknowledge the affected message until durable handling exists.

Ordering and range preservation

Task 5 must preserve enough ordered-range data to support later replay and checkpoint advancement.

Minimum range metadata to persist per durable raw batch:

  • first and last Debezium source coordinates in batch,
  • first and last normalized production ordering tokens in batch,
  • first and last SQS sequence or ordering values in batch,
  • message count,
  • duplicate or redelivery notes when safe duplicates were collapsed,
  • source connector or source identity if available.

This does not replace full Task 8 checkpointing, but it freezes what raw-stage durability must preserve now so Task 6 can validate production order and Task 8 can advance checkpoints from stored facts rather than from transient worker memory.

Failure boundary

Task 5 must preserve feature-doc boundary between record-local quarantine and batch-level failure.

  • malformed or unmapped records should already be quarantined before raw write,
  • raw S3 write failure is batch-level failure for the resolved tenant/entity slice,
  • durable job creation failure is batch-level failure for the resolved tenant/entity slice,
  • good slices may still succeed even if another slice from same poll window fails.

Task 5 must not reintroduce all-or-nothing failure across unrelated tenants or entities from same worker batch.

Fit with pipeline lineage

Task 5 establishes the first durable KDS CDC stage batch in the framework lifecycle.

Task 5 should preserve these expectations for later stages:

  • extract_raw produces an EtlStageResult,
  • downstream normalize_load uses upstream raw batch metadata and RawS3Path,
  • later stages chain through UpstreamBatchId,
  • Task 8 can advance checkpoints after documented success points.

Proposed Implementation Shape

Recommended high-level flow:

text
Task 2 worker closes ordered micro-batch
  -> Task 4 resolution filters to resolved records
  -> split resolved records by company_id + tenant_id + entity
  -> for each slice:
       start KDS CDC extract_raw stage batch
       serialize source-faithful raw payload
       write raw object to ETL S3
       persist RawS3Path + coordinate/sequence ranges + retry metadata
       complete stage batch
        mark only that slice's records safe for SQS delete
   -> delete only receipt handles for records whose slice raw write and durable metadata both succeeded

Recommended raw writer contract shape:

csharp
public interface IKdsCdcRawBatchWriter
{
    Task<KdsCdcRawBatchWriteResult> WriteAsync(
        KdsCdcRawBatchWriteRequest request,
        CancellationToken ct = default);
}

Recommended request shape:

text
KdsCdcRawBatchWriteRequest
  CompanyId
  TenantId
  TenantDatabase
  Entity
  BusinessDate
  Records[]
  FirstSourceCoordinate
  LastSourceCoordinate
  FirstProductionOrderingToken
  LastProductionOrderingToken
  FirstSqsSequence
  LastSqsSequence
  WorkerInstanceId

Recommended durable details shape:

text
KdsCdcExtractBatchDetails
  CompanyId
  TenantId
  TenantDatabase
  Entity
  RawS3Path
  RecordCount
  SourceCoordinateRange
  ProductionOrderingTokenRange
  SqsSequenceRange
  MessageIds or acknowledgement token summary
  WorkerAttempt
  SerializationFormat

Recommended stage identity:

csharp
new CreateIngestionBatchRequest(
    tenantId,
    tenantDatabase,
    IngestionMode.Cdc,
    IngestionSourceType.KdsCdc,
    CompanyId: companyId,
    BusinessDate: businessDate,
    PipelineStage: EtlStageNames.ExtractRaw)

Important design notes:

  • prefer one durable extract_raw batch per resolved tenant/entity slice,
  • use existing ETL S3 layout and storage abstraction,
  • for production traffic, derive and persist comparable ordering tokens from Debezium/source coordinates before any delete acknowledgement decision; never promote SQS FIFO sequence into source-of-truth ordering proof,
  • keep receipt handles or delete tokens outside raw object body if they are transport-only, but persist enough acknowledgement summary in durable metadata to prove safe delete set,
  • keep DetailsJson structured and queryable enough for tests and future replay tooling,
  • avoid relying on transient in-memory ordering state after S3 write finishes.

Testing

Unit tests

Add tests for:

  • raw serializer preserves original CDC envelope and required replay metadata,
  • production raw serializer preserves both original Debezium coordinates and normalized comparable ordering tokens,
  • raw writer builds S3 raw keys under company_id, tenant_id, source_type=kds_cdc, batch_id, and entity,
  • raw writer uses processing-date business date,
  • durable details include raw path, entity, record count, coordinate range, and SQS range,
  • durable details include production ordering-token range and never rely on SQS SequenceNumber alone for production ordering proof,
  • one resolved tenant/entity slice produces one stage batch and one raw artifact path under current schema assumptions,
  • mixed-entity or mixed-tenant input is split before durable batch persistence,
  • successful raw write plus durable metadata marks messages safe for delete,
  • raw-write failure leaves messages unacknowledged,
  • production message with missing or unparseable ordering-token fields is not acknowledged,
  • durable-metadata failure after raw write also leaves messages unacknowledged.

Data/repository tests

Add tests for:

  • IngestionBatch persistence stores RawS3Path and KDS CDC structured DetailsJson correctly,
  • any additive child metadata entity persists coordinate ranges and acknowledgement summary correctly,
  • stage-batch creation uses IngestionMode.Cdc, IngestionSourceType.KdsCdc, PipelineStage = extract_raw, and processing-date BusinessDate.

Integration/composition tests

Add tests proving:

  • resolved KDS CDC slice flows through ETL raw storage and stage-batch lifecycle successfully,
  • IEtlS3Storage.WriteRawAsync(...) is invoked before SQS delete,
  • SetRawS3PathAsync(...) or equivalent durable raw-path persistence happens before acknowledgement,
  • worker does not delete messages when durable batch creation or completion fails,
  • downstream handoff can reopen raw object using existing IRawObjectReader path style,
  • current Brink raw path behavior remains unchanged.

Not required in task 5

This task does not require:

  • normalization to load Parquet,
  • Redshift COPY,
  • final checkpoint advancement behavior,
  • curated merge/export,
  • full replay orchestration UI or CLI.

Acceptance Criteria

  • [ ] Accepted KDS CDC records are written to tenant/entity-scoped ETL raw S3 paths
  • [ ] Raw object contents preserve original CDC envelopes and replay-safe source metadata
  • [ ] KDS CDC extract_raw uses IngestionMode.Cdc, IngestionSourceType.KdsCdc, and shared EtlStageNames.ExtractRaw
  • [ ] Durable internal batch or job metadata is created only after raw write succeeds
  • [ ] Durable metadata includes raw path, tenant identity, tenant database, entity, and source/SQS ordering ranges
  • [ ] Production durable metadata includes comparable ordering-token ranges derived from Debezium/source coordinates
  • [ ] Current schema is used safely with one durable raw batch per tenant/entity slice, or additive metadata storage is introduced deliberately where needed
  • [ ] SQS messages are deleted only after both raw persistence and durable resume state succeed
  • [ ] Production ordering proof never relies on SQS SequenceNumber alone when Debezium/source-coordinate ordering cannot be derived
  • [ ] Partial success deletes only receipt handles for successfully persisted/quarantined records, never an entire mixed poll window by accident
  • [ ] Raw-write failure leaves SQS messages unacknowledged
  • [ ] Durable-metadata failure after raw write also leaves SQS messages unacknowledged
  • [ ] Good tenant/entity slices can succeed even when another slice from same poll window fails
  • [ ] Tests cover raw payload fidelity, durable metadata, split boundaries, and acknowledgement gating

Notes

  • Follow docs/features/ETL/kds-cdc/index.md#stage-4-store-to-s3-raw-extract_raw and #stage-5-durable-internal-job-creation as source of truth.
  • Keep S3 raw as replay source of truth after acknowledgement; never depend on SQS retention for replay.
  • Current repo storage and batch model strongly favor one KDS CDC extract_raw batch per tenant/entity slice.
  • Use Brink extract_raw flow as structural reference only; do not inherit Brink source semantics or business-date rules.
  • Leave final checkpoint advancement and replay controls to Task 8, but persist enough raw-batch facts now that Task 8 can build on durable state rather than worker memory.