Source-timestamp ordering with a per-instance cap of one
Rule 9 · applies to the subscriber · Back to all rules
Breaks a guarantee. A single per-instance slot cannot hold enough samples to reorder by source time, so earlier-stamped samples are lost. Fast DDS does not support source-timestamp ordering.
- Settings involved: Resource Limits and Destination Order
- What QoS Guard checks:
[DESTORD = BY_SOURCE] ∧ [HIST.kind = KEEP_ALL] ∧ [RESLIM.mpi = 1]
Example
KEEP_ALL with max_samples_per_instance 1 and source ordering behaves like depth 1, which defeats reordering.
How to fix it
Raise max_samples_per_instance above 1 when using source-timestamp ordering. On Fast DDS, avoid BY_SOURCE_TIMESTAMP.
Why this rule is flagged
What the DDS specification says
The DDS specification does not settle this case on its own, so the rule rests on the engine's implementation.
What the engine source code shows
Fast DDS does not support BY_SOURCE_TIMESTAMP for subscribers in the tested versions; profiles were not created or matched.
Cyclone DDS implements source-timestamp ordering in the reader cache. With KEEP_ALL and max_samples_per_instance = 1, only one sample is retained per instance, so source ordering has little comparative history to operate on.
Cyclone DDS implementation evidence
// Set to Keep_all, but limited to 1 due to max_samples_per_instance
/* Check if resource max_samples QoS exceeded */
if (rhc->reader && rhc->max_samples != DDS_LENGTH_UNLIMITED && rhc->n_vsamples >= (uint32_t) rhc->max_samples)
{
cb_data->raw_status_id = (int) DDS_SAMPLE_REJECTED_STATUS_ID;
cb_data->extra = DDS_REJECTED_BY_SAMPLES_LIMIT;
cb_data->handle = inst->iid;
cb_data->add = true; return false;
}
/* Check if resource max_samples_per_instance QoS exceeded */
if (rhc->reader && rhc->max_samples_per_instance != DDS_LENGTH_UNLIMITED && inst->nvsamples >= (uint32_t) rhc->max_samples_per_instance)
{
cb_data->raw_status_id = (int) DDS_SAMPLE_REJECTED_STATUS_ID;
cb_data->extra = DDS_REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
cb_data->handle = inst->iid;
cb_data->add = true; return false;
}
| Item | Value |
|---|---|
| Dataset | Download CSV |
| Fixed QoS setting | HIST.kind = KEEP_ALL |
| Tested variable | RESLIM.mpi, DESTORD.kind |
| Tested values | RESLIM.mpi ∈ {0, 1, 2}, DESTORD ∈ {BY_RECEPTION_TIMESTAMP, BY_SOURCE_TIMESTAMP} |
| Rule-relevant case | DESTORD = BY_SOURCE_TIMESTAMP, HIST.kind = KEEP_ALL |
| Tested engines / versions | Fast DDS 2.6.11 (Humble), Fast DDS 2.14.6 (Jazzy), Cyclone DDS 0.10.5 |
| Network setting | RTT ∈ {1 ms, 50 ms}, loss = 0%, PP = 50 ms, message size = 1024 B |
| Engine | Tested setting | Observed behavior |
|---|---|---|
| Fast DDS 2.6.11 (Humble) | HIST.kind = KEEP_ALL, RESLIM.mpi ∈ {0, 1, 2}, DESTORD = BY_SOURCE_TIMESTAMP |
Profile not created or matched (BY_SOURCE_TIMESTAMP unsupported in the tested Fast DDS versions) |
| Fast DDS 2.14.6 (Jazzy) | HIST.kind = KEEP_ALL, RESLIM.mpi ∈ {0, 1, 2}, DESTORD = BY_SOURCE_TIMESTAMP |
Profile not created or matched (BY_SOURCE_TIMESTAMP unsupported in the tested Fast DDS versions) |
| Cyclone DDS 0.10.5 | HIST.kind = KEEP_ALL, RESLIM.mpi ∈ {0, 1, 2}, DESTORD = BY_SOURCE_TIMESTAMP |
Profile accepted, matched, and delivered for RESLIM.mpi ∈ {1, 2}; no delivery for RESLIM.mpi = 0 |
What you'll see when you run it
QoS Guard flags this as a functional conflict:
[FUNCTIONAL] [cloud_sub] [SUB] DESTORD=BY_SOURCE + KEEP_ALL + mpi=1 <-> reorder buffer
At runtime a per-instance cap of one makes KEEP_ALL behave like a depth of one, so the reader holds a single sample per instance. An engine that orders by source timestamp then has no earlier sample to compare against, and a late but earlier-stamped update is dropped instead of reordered. Fast DDS does not support BY_SOURCE_TIMESTAMP, so the profile is rejected at creation instead.