fix: 过滤观察流缺失身份事件

This commit is contained in:
root
2026-07-16 05:52:22 +02:00
parent 33908cd4fa
commit b8e1f167b9
2 changed files with 25 additions and 11 deletions
@@ -41,7 +41,7 @@ test("retention handoff replays once, deduplicates overlap and late pre-barrier
"buffered-live:4:hwlab:evt_0_4",
"connected:1"
]);
assert.deepEqual(summary.counts, { matched: 3, filteredNonHwlab: 0, replayed: 3, buffered: 3, bufferedDelivered: 2, liveDelivered: 0, deduplicated: 1, resumeSkipped: 0 });
assert.deepEqual(summary.counts, { matched: 3, filteredNonHwlab: 0, filteredMissingIdentity: 0, replayed: 3, buffered: 3, bufferedDelivered: 2, liveDelivered: 0, deduplicated: 1, resumeSkipped: 0 });
publishLive(record(2).value, transport(2));
publishLive(record(5).value, transport(5));
@@ -101,7 +101,7 @@ test("retention emits handoff before buffered live flush and connected live", as
assert.deepEqual(phases, ["replay:0", "replay:1", "handoff", "buffered-live:2", "live"]);
});
test("unscoped observer filters legacy non-HWLAB records without weakening scoped handoff", async () => {
test("unscoped observer filters non-deliverable history without weakening scoped handoff", async () => {
let publishLive: any = null;
const delivered: string[] = [];
const legacy = { ...record(0), value: { schema: "platform-infra.kafka.shadow-produce.v1", eventId: "legacy-smoke" } };
@@ -111,16 +111,17 @@ test("unscoped observer filters legacy non-HWLAB records without weakening scope
subscribeLive(listener: any) { publishLive = listener; return () => {}; },
async queryRetention() {
publishLive({ schema: "platform-infra.kafka.shadow-produce.v1", eventId: "legacy-live" }, transport(2));
return queryResult([legacy, record(1)], [{ partition: 0, startOffset: "0", endOffset: "2" }]);
return queryResult([legacy, { ...record(1), value: { schema: "hwlab.event.v1", eventType: "legacy-without-identity" } }, record(2)], [{ partition: 0, startOffset: "0", endOffset: "3" }]);
},
async deliverEvent(envelope: any) { delivered.push(envelope.eventId); return true; },
async deliverConnected() { return true; }
});
const summary = await handoff.start();
assert.deepEqual(delivered, ["hwlab:evt_0_1"]);
assert.equal(summary.counts.matched, 2);
assert.deepEqual(delivered, ["hwlab:evt_0_2"]);
assert.equal(summary.counts.matched, 3);
assert.equal(summary.counts.filteredNonHwlab, 2);
assert.equal(summary.counts.filteredMissingIdentity, 1);
assert.equal(summary.counts.replayed, 1);
});
@@ -35,6 +35,7 @@ export function createWorkbenchKafkaRefreshHandoff(options = {}) {
const counts = {
matched: 0,
filteredNonHwlab: 0,
filteredMissingIdentity: 0,
replayed: 0,
buffered: 0,
bufferedDelivered: 0,
@@ -101,9 +102,12 @@ export function createWorkbenchKafkaRefreshHandoff(options = {}) {
function onLiveEnvelope(envelope, transport) {
if (phase === "closed" || phase === "failed") return;
if (allowUnscoped && envelope?.schema !== "hwlab.event.v1") {
counts.filteredNonHwlab += 1;
return;
if (allowUnscoped) {
const filtered = unscopedEnvelopeFilter(envelope);
if (filtered) {
counts[filtered] += 1;
return;
}
}
if (!envelopeMatchesScope(envelope, sessionId, traceId)) return;
if (phase !== "live") {
@@ -192,9 +196,12 @@ export function createWorkbenchKafkaRefreshHandoff(options = {}) {
const normalized = [];
for (const record of records) {
const envelope = record?.value;
if (allowUnscoped && envelope?.schema !== "hwlab.event.v1") {
counts.filteredNonHwlab += 1;
continue;
if (allowUnscoped) {
const filtered = unscopedEnvelopeFilter(envelope);
if (filtered) {
counts[filtered] += 1;
continue;
}
}
if (!envelopeMatchesScope(envelope, sessionId, traceId) || envelope?.schema !== "hwlab.event.v1") {
throw handoffError("workbench_kafka_refresh_scope_mismatch", "Kafka retention query returned an envelope outside the authorized scope.", phase);
@@ -450,6 +457,12 @@ function envelopeMatchesScope(envelope, sessionId, traceId) {
return true;
}
function unscopedEnvelopeFilter(envelope) {
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope) || envelope.schema !== "hwlab.event.v1") return "filteredNonHwlab";
if (!textValue(envelope.eventId) && !textValue(envelope.sourceEventId)) return "filteredMissingIdentity";
return null;
}
function requireTransportIdentity(value, expectedTopic, phase) {
const topic = textValue(value?.topic);
const partition = partitionValue(value?.partition);