You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parent epic: #337
Delivery step 7; after file-backed DLR storage #340 and before PostgreSQL outbound durability #333
Status
Delivery step 7 in #337. Begin this issue after #340 is merged and the shared DLR backend-selection, readiness, and operation-instrumentation structure is stable.
This issue is not a blocker for filesystem SMS pending storage in #339, file-backed router/routed state in #345, or the database-free filesystem profile completed by #340.
Problem
Sendium supports durable DLR correlation and downstream delivery through PostgreSQL and, after #340, file-backed storage. Some deployments do not require DLR state to survive restart and prefer bounded process-local handling without opening either durable DLR store.
Disabling DLR handling entirely is not equivalent: disabled mode does not correlate provider message IDs or deliver HTTP and downstream SMPP receipts while the process is running.
Goal
Implement a configurable, bounded in-memory backend for the existing DlrStorage behavior.
Memory mode must provide normal DLR correlation and downstream delivery while Sendium remains active, with explicit capacity and retention limits. It is intentionally non-durable, single-process, and unsuitable for multiple replicas.
This issue does not change the default DLR backend. PostgreSQL and file-backed DLR storage remain the durable choices.
Durability boundary
Outbound-message durability and DLR durability remain separate selections:
Outbound storage determines whether an accepted SMS survives until provider processing reaches a terminal outcome.
With filesystem outbound SMS storage from #339 and #345 and memory DLR storage, an accepted SMS remains recoverable until provider processing and the required in-memory DLR handoff complete. After the pending SMS file is removed, a restart may lose its provider correlation or pending downstream receipt.
With memory SMS processing and memory DLR storage, both boundaries are process-local. After #333, PostgreSQL outbound storage may also be combined with memory DLR storage where that profile is explicitly supported.
memory: bounded process-local handling from this issue.
disabled: no Sendium-owned DLR correlation or downstream delivery.
Backend selection is startup-only. Selecting memory must not initialize a PostgreSQL DLR datasource or open the file-backed DLR store. Unsupported SMS/DLR profile combinations fail startup rather than silently weakening durability.
Functional behavior
While the process remains active, memory mode must:
Correlate provider message IDs with gateway message IDs.
Support multiple provider IDs for one multipart submission.
Apply first-terminal-DLR-wins semantics.
Preserve intermediate provider states without prematurely consuming correlations.
Create provider-rejection results for supported downstream channels.
Deliver and retry HTTP callbacks.
Deliver downstream SMPP receipts.
Retain pending SMPP receipts while the client is disconnected.
Persist delivery-attempt fencing in memory so stale callbacks cannot complete or retry a newer attempt.
Remove expired correlations and pending deliveries through periodic cleanup.
Never fall back silently to disabled or another backend after initialization failure.
All correlations, reservations, callbacks, retries, attempt state, and pending SMPP receipts are lost when the process stops.
Capacity and admission
Capacity counts logical gateway messages, not provider-correlation entries. A multipart message with multiple provider message IDs consumes one tracked-message slot.
Reservation must be atomic under concurrent submissions and must never exceed the configured maximum.
For a new HTTP or SMPP submission requesting a DLR:
Reserve one memory DLR slot.
Admit the SMS through the selected outbound backend.
Send protocol success only after both operations succeed.
Release the reservation if outbound admission fails.
If no slot is available, reject or backpressure the DLR-requesting submission with the configured protocol response. Non-DLR submissions continue subject to normal outbound limits.
After restart, the memory backend starts empty. A pending SMS recovered from filesystem or PostgreSQL outbound storage must acquire a new DLR reservation before provider submission. If capacity is unavailable, keep the outbound record pending and retry reservation later rather than submitting an SMS whose requested DLR cannot be tracked.
Do not silently evict active entries to make room. Release a reservation when:
Processing terminates without requiring further DLR state.
The provider correlation expires before terminal resolution.
The downstream DLR is delivered successfully.
A pending downstream delivery expires.
Another terminal path explicitly proves that no correlation or downstream delivery remains.
The initial three-day provider-correlation and seven-day pending-delivery values preserve current PostgreSQL retention behavior. Final defaults must be checked against the heap cost of representative DLR records.
Observability
Logs
Log the selected DLR backend, configured capacity, retention periods, and cleanup interval at startup.
Emit a prominent warning that memory-backed DLR state is non-durable and single-process.
Log capacity rejections and periodic cleanup summaries.
Do not log message payloads, provider message IDs, or other sensitive message data.
Readiness
Expose the selected backend through the existing sendium-dlr-storage readiness check.
Memory mode remains UP while the backend is initialized and internally operational. Reaching configured capacity does not make the entire service unready because non-DLR submissions may still be accepted.
Metrics
Expose only:
The selected backend, tagged with backend=memory.
Storage operation latency and success/error outcome.
Current reserved logical-message capacity and configured maximum capacity.
Do not introduce separate expiration, cleanup, rejection, correlation, or delivery-state metrics in this issue.
Verification
Focused DlrStorage behavior tests run against memory mode.
Concurrency tests prove reservation count never exceeds capacity.
Admission tests prove capacity is reserved before HTTP or SMPP success and released when outbound admission fails.
Restart tests prove memory DLR state is cleared while pending durable SMS records remain recoverable.
Recovery tests prove a durable pending SMS reacquires capacity before provider submission.
Parent epic: #337
Delivery step 7; after file-backed DLR storage #340 and before PostgreSQL outbound durability #333
Status
Delivery step 7 in #337. Begin this issue after #340 is merged and the shared DLR backend-selection, readiness, and operation-instrumentation structure is stable.
This issue is not a blocker for filesystem SMS pending storage in #339, file-backed router/routed state in #345, or the database-free filesystem profile completed by #340.
Problem
Sendium supports durable DLR correlation and downstream delivery through PostgreSQL and, after #340, file-backed storage. Some deployments do not require DLR state to survive restart and prefer bounded process-local handling without opening either durable DLR store.
Disabling DLR handling entirely is not equivalent: disabled mode does not correlate provider message IDs or deliver HTTP and downstream SMPP receipts while the process is running.
Goal
Implement a configurable, bounded in-memory backend for the existing
DlrStoragebehavior.Memory mode must provide normal DLR correlation and downstream delivery while Sendium remains active, with explicit capacity and retention limits. It is intentionally non-durable, single-process, and unsuitable for multiple replicas.
This issue does not change the default DLR backend. PostgreSQL and file-backed DLR storage remain the durable choices.
Durability boundary
Outbound-message durability and DLR durability remain separate selections:
With filesystem outbound SMS storage from #339 and #345 and memory DLR storage, an accepted SMS remains recoverable until provider processing and the required in-memory DLR handoff complete. After the pending SMS file is removed, a restart may lose its provider correlation or pending downstream receipt.
With memory SMS processing and memory DLR storage, both boundaries are process-local. After #333, PostgreSQL outbound storage may also be combined with memory DLR storage where that profile is explicitly supported.
Backend selection
The DLR backend selector must support:
postgresql: existing durable PostgreSQL handling.file: durable single-process local handling from feat(storage): implement file-backed DLR storage #340.memory: bounded process-local handling from this issue.disabled: no Sendium-owned DLR correlation or downstream delivery.Backend selection is startup-only. Selecting
memorymust not initialize a PostgreSQL DLR datasource or open the file-backed DLR store. Unsupported SMS/DLR profile combinations fail startup rather than silently weakening durability.Functional behavior
While the process remains active, memory mode must:
disabledor another backend after initialization failure.All correlations, reservations, callbacks, retries, attempt state, and pending SMPP receipts are lost when the process stops.
Capacity and admission
Capacity counts logical gateway messages, not provider-correlation entries. A multipart message with multiple provider message IDs consumes one tracked-message slot.
Reservation must be atomic under concurrent submissions and must never exceed the configured maximum.
For a new HTTP or SMPP submission requesting a DLR:
If no slot is available, reject or backpressure the DLR-requesting submission with the configured protocol response. Non-DLR submissions continue subject to normal outbound limits.
After restart, the memory backend starts empty. A pending SMS recovered from filesystem or PostgreSQL outbound storage must acquire a new DLR reservation before provider submission. If capacity is unavailable, keep the outbound record pending and retry reservation later rather than submitting an SMS whose requested DLR cannot be tracked.
Do not silently evict active entries to make room. Release a reservation when:
Configuration
Configuration is equivalent to:
Suggested environment-variable mappings:
The initial three-day provider-correlation and seven-day pending-delivery values preserve current PostgreSQL retention behavior. Final defaults must be checked against the heap cost of representative DLR records.
Observability
Logs
Readiness
Expose the selected backend through the existing
sendium-dlr-storagereadiness check.Memory mode remains
UPwhile the backend is initialized and internally operational. Reaching configured capacity does not make the entire service unready because non-DLR submissions may still be accepted.Metrics
Expose only:
backend=memory.Do not introduce separate expiration, cleanup, rejection, correlation, or delivery-state metrics in this issue.
Verification
DlrStoragebehavior tests run against memory mode.Acceptance criteria
memoryis a selectable DLR backend and does not require a PostgreSQL datasource or file-backed DLR store.DlrStoragebehavior.Open decisions
sendium.dlr.persistence.enabled.Non-goals