openvibers/openvibe.report Commits

The shadow leaves today's still-counting day rows out (REP-2) (#92)

OP OpenVibers committed 11 days ago · 85b0d7c817 · 2 files, +20 −0

After #91 the shadow's first run had 3 differences, all today's pageviews (340 against 346).
Network answers each window from its own cache, so its 7- and 90-day answers name different
numbers for today, minutes apart, and the day row Report kept is the last one ingested. A day still
counting is not a difference; every finished day is still compared.

Changes

ddiff --git a/server/analytics/shadow.js b/server/analytics/shadow.js
iindex 79107a6..62aa969 100644
---- a/server/analytics/shadow.js
++++ b/server/analytics/shadow.js
@@@ -7,6 +7,10 @@
* The two sides are normalized with the same function the ingest uses (metricRowsFromStats), so an equal answer
* compares equal; a changed number, a missing one or an extra stale one is a difference. Billing is not in the
* shadow: Report quotes Billing directly and never keeps a second copy of money.
++ *
++ * Today's day rows are not compared: the day is still counting, and Network answers each window from its own cache,
++ * so its 7-day and 90-day answers name different numbers for today, minutes apart; the one Report kept last is not
++ * a difference (11-03: the only three left, today's pageviews, 340 against 346). Every finished day is compared.
*/
const crypto = require('crypto');
const { metricRowsFromStats } = require('./ingest');
@@@ -73,10 +77,12 @@ function createShadow({ store, network, now = () => Date.now(), log = console })
network_json: networkJson, report_json: reportJson ? reportJson.value : null,
});
}
++ const today = new Date(now()).toISOString().slice(0, 10);
for (const key of keys) {
const net = networkRows.find((r) => keyOf(r) === key) || null;
const rep = reportByKey.get(key) || null;
if (!net && !rep) continue;
++ if ((net || rep).kind === 'day' && (net || rep).day === today) continue; // still counting (see above)
// Only compare a stored number whose metric Network actually reports in this window; an unrelated
// stored scope is not a shadow difference.
if (!net && !netKeys.has(key) && !networkRows.some((r) => r.metric === (rep && rep.metric))) continue;
ddiff --git a/test/shadow.test.js b/test/shadow.test.js
iindex f5e6335..1087b3a 100644
---- a/test/shadow.test.js
++++ b/test/shadow.test.js
@@@ -55,6 +55,20 @@ const { stats, NOW } = require('./fixtures/analytics');
assert.strictEqual(await t.ctx.store.db.value(`SELECT value::int AS v FROM report_metric_windows WHERE metric = 'pageviews' AND scope = 'network' AND window_days = 7`), 307, 'and Report took it in');
});
++ await check('today is still counting: its day rows are not compared; a finished day still is', async () => {
++ const days = stats.pageviews_daily.map((p) => p.day);
++ const today = new Date(NOW).toISOString().slice(0, 10);
++ assert.strictEqual(days[days.length - 1], today, 'the fixture ends today');
++ const moved = JSON.parse(JSON.stringify(stats));
++ moved.pageviews_daily[moved.pageviews_daily.length - 1].n += 6; // another window's later snapshot of today
++ const same = await t.ctx.shadow.compare({ days: 7, stats: moved });
++ assert.ok(!same.diffs.some((d) => d.day === today), JSON.stringify(same.diffs));
++ const old = JSON.parse(JSON.stringify(stats));
++ old.pageviews_daily[0].n += 1;
++ const past = await t.ctx.shadow.compare({ days: 7, stats: old });
++ assert.ok(past.diffs.some((d) => d.metric === 'pageviews' && d.day === days[0]), 'a finished day that differs is a difference');
++ });
++
await check('a Network that does not answer is a run with no differences, never a clean claim', async () => {
const down = await t.ctx.shadow.compare({ days: 7, stats: null });
assert.strictEqual(down.status, 'network_unavailable');