mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
Accept authoritative empty vessel coverage without an outage
This commit is contained in:
1 parent
89e566aa9b
commit
27a986f40f
5 files changed
+67
-4
No files matched your search
@@ -1,5 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
- Treat explicitly current, complete empty vessel coverage as a successful update,
|
||||||
|
clearing obsolete contacts while retaining stale or incomplete snapshots.
|
||||||
|
|
||||||
- Custom Street Level provider switches survive share links and stored state
|
- Custom Street Level provider switches survive share links and stored state
|
||||||
using URL-safe stable provider IDs and a shared boolean-switch codec; existing
|
using URL-safe stable provider IDs and a shared boolean-switch codec; existing
|
||||||
Mapillary links keep their meaning. Explicit provider toggles reach durable
|
Mapillary links keep their meaning. Explicit provider toggles reach durable
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# God's Eye View Current State
|
# God's Eye View Current State
|
||||||
|
|
||||||
|
Vessel sources can report healthy empty coverage using a current, complete
|
||||||
|
snapshot with zero raw rows, a healthy transport and a positive last-message
|
||||||
|
time. This clears obsolete contacts and settles startup without manufacturing
|
||||||
|
position timestamps. Stale, incomplete and malformed empty results retain the
|
||||||
|
existing recovery behavior.
|
||||||
|
|
||||||
Catalog source contracts and their consuming layers are declared in
|
Catalog source contracts and their consuming layers are declared in
|
||||||
`src/app/sourceComposition.js`. Omitted or null acquisition sources preserve layer
|
`src/app/sourceComposition.js`. Omitted or null acquisition sources preserve layer
|
||||||
registration and serialization, report unavailable, and are refused by the existing
|
registration and serialization, report unavailable, and are refused by the existing
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export function createIngestion({
|
|||||||
feed.acceptedRowCount = snapshot.acceptedRowCount;
|
feed.acceptedRowCount = snapshot.acceptedRowCount;
|
||||||
feed.partial = payload?.complete === false;
|
feed.partial = payload?.complete === false;
|
||||||
|
|
||||||
if (snapshot.acceptedRowCount === 0) {
|
if (snapshot.acceptedRowCount === 0 && !snapshot.emptyCoverage) {
|
||||||
feed.count = readCount();
|
feed.count = readCount();
|
||||||
feed.stale = feed.count > 0 || Boolean(payload?.refreshing);
|
feed.stale = feed.count > 0 || Boolean(payload?.refreshing);
|
||||||
if (isDefinitiveTransportFailure(snapshot.transportStatus)) {
|
if (isDefinitiveTransportFailure(snapshot.transportStatus)) {
|
||||||
|
|||||||
@@ -361,3 +361,47 @@ test('partial vessel snapshots preserve freshness and recover after a complete u
|
|||||||
layer.destroy();
|
layer.destroy();
|
||||||
assert.equal(layer.getStats().partial, false);
|
assert.equal(layer.getStats().partial, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('authoritative empty coverage clears old contacts without declaring an outage', async () => {
|
||||||
|
let current = snapshot([observation('111')]);
|
||||||
|
const { layer } = setup({
|
||||||
|
async getSnapshot() {
|
||||||
|
return current;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await layer.update();
|
||||||
|
current = snapshot([], { observedAtMs: null, lastMessageAt: 500 });
|
||||||
|
await layer.update();
|
||||||
|
assert.ok(!layer.hasContact('111'));
|
||||||
|
assert.equal(layer.getStats().count, 0);
|
||||||
|
assert.equal(layer.getStats().error, null);
|
||||||
|
assert.equal(layer.getStats().stale, false);
|
||||||
|
assert.equal(layer.getStats().lastUpdate, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty coverage cannot hide stale, incomplete or rejected source data', async () => {
|
||||||
|
for (const patch of [
|
||||||
|
{ freshness: 'unknown' },
|
||||||
|
{ freshness: 'stale' },
|
||||||
|
{ complete: false },
|
||||||
|
{ rawRowCount: 1 },
|
||||||
|
{ transportStatus: 'stale' },
|
||||||
|
{ lastMessageAt: null },
|
||||||
|
]) {
|
||||||
|
let current = snapshot([observation('111')]);
|
||||||
|
const { layer } = setup({
|
||||||
|
async getSnapshot() {
|
||||||
|
return current;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await layer.update();
|
||||||
|
current = snapshot([], {
|
||||||
|
observedAtMs: null,
|
||||||
|
lastMessageAt: 500,
|
||||||
|
...patch,
|
||||||
|
});
|
||||||
|
await layer.update();
|
||||||
|
assert.equal(layer.hasContact('111'), true, JSON.stringify(patch));
|
||||||
|
assert.ok(layer.getStats().error, JSON.stringify(patch));
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -119,7 +119,16 @@ export function createQueries({
|
|||||||
typeof payload?.status === 'string' ? payload.status : null;
|
typeof payload?.status === 'string' ? payload.status : null;
|
||||||
const lastMessageAt = payload?.lastMessageAt ?? null;
|
const lastMessageAt = payload?.lastMessageAt ?? null;
|
||||||
const acceptedRowCount = acceptedRows.length;
|
const acceptedRowCount = acceptedRows.length;
|
||||||
|
const emptyCoverage =
|
||||||
|
rawRows.length === 0 &&
|
||||||
|
payload?.rawRowCount === 0 &&
|
||||||
|
payload?.complete === true &&
|
||||||
|
payload?.freshness === 'current' &&
|
||||||
|
AIS_HEALTHY_STATUSES.has(transportStatus) &&
|
||||||
|
Number.isFinite(Number(lastMessageAt)) &&
|
||||||
|
Number(lastMessageAt) > 0;
|
||||||
return {
|
return {
|
||||||
|
emptyCoverage,
|
||||||
transportStatus,
|
transportStatus,
|
||||||
lastMessageAt,
|
lastMessageAt,
|
||||||
rawRows,
|
rawRows,
|
||||||
@@ -130,9 +139,10 @@ export function createQueries({
|
|||||||
? payload.rawRowCount
|
? payload.rawRowCount
|
||||||
: rawRows.length,
|
: rawRows.length,
|
||||||
acceptedRowCount,
|
acceptedRowCount,
|
||||||
error:
|
error: emptyCoverage
|
||||||
deriveAisFeedError(payload, acceptedRowCount) ||
|
? null
|
||||||
(acceptedRowCount === 0 ? 'awaiting usable AIS positions…' : null),
|
: deriveAisFeedError(payload, acceptedRowCount) ||
|
||||||
|
(acceptedRowCount === 0 ? 'awaiting usable AIS positions…' : null),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user