Skip to content

Commit 8c659cd

Browse files
committed
fix: Reduce overzealous index event emission
1 parent 0c5114e commit 8c659cd

File tree

3 files changed

+40
-51
lines changed

3 files changed

+40
-51
lines changed

contract-tests/service.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def status():
7474
'event-sampling',
7575
'polling-gzip',
7676
'inline-context-all',
77+
'reduce-index-events',
7778
'anonymous-redaction',
7879
'evaluation-hooks',
7980
'omit-anonymous-contexts',

ldclient/impl/events/event_processor.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,15 +358,16 @@ def _process_event(self, event: EventInput):
358358
elif isinstance(event, MigrationOpEvent):
359359
full_event = event
360360

361-
self._get_indexable_context(event, lambda c: self._outbox.add_event(IndexEvent(event.timestamp, c)))
361+
suppress_index = full_event is not None or debug_event is not None
362+
self._get_indexable_context(event, suppress_index, lambda c: self._outbox.add_event(IndexEvent(event.timestamp, c)))
362363

363364
if full_event and self._sampler.sample(sampling_ratio):
364365
self._outbox.add_event(full_event)
365366

366367
if debug_event and self._sampler.sample(sampling_ratio):
367368
self._outbox.add_event(debug_event)
368369

369-
def _get_indexable_context(self, event: EventInput, block: Callable[[Context], None]):
370+
def _get_indexable_context(self, event: EventInput, suppress_index: bool, block: Callable[[Context], None]):
370371
if event.context is None:
371372
return
372373

@@ -381,7 +382,7 @@ def _get_indexable_context(self, event: EventInput, block: Callable[[Context], N
381382
if already_seen:
382383
self._deduplicated_contexts += 1
383384
return
384-
elif isinstance(event, EventInputIdentify) or isinstance(event, MigrationOpEvent) or isinstance(event, EventInputCustom):
385+
elif suppress_index:
385386
return
386387

387388
block(context)

ldclient/testing/impl/events/test_event_processor.py

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,15 @@ def test_omit_anonymous_contexts_strips_anonymous_contexts_correctly():
282282
check_identify_event(output[0], e, formatter.format_context(c))
283283

284284

285-
def test_individual_feature_event_is_queued_with_index_event():
285+
def test_no_index_emitted_for_tracked_evaluation():
286286
with DefaultTestProcessor() as ep:
287287
e = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value', None, 'default', None, True)
288288
ep.send_event(e)
289289

290290
output = flush_and_get_events(ep)
291-
assert len(output) == 3
292-
check_index_event(output[0], e)
293-
check_feature_event(output[1], e)
294-
check_summary_event(output[2])
291+
assert len(output) == 2
292+
check_feature_event(output[0], e)
293+
check_summary_event(output[1])
295294

296295

297296
def test_omit_anonymous_context_emits_feature_event_without_index():
@@ -312,14 +311,13 @@ def test_omit_anonymous_context_strips_anonymous_from_index_event():
312311
b = Context.builder('b').kind('b').anonymous(True).build()
313312
c = Context.builder('c').kind('c').anonymous(False).build()
314313
mc = Context.multi_builder().add(a).add(b).add(c).build()
315-
e = EventInputEvaluation(timestamp, mc, flag.key, flag, 1, 'value', None, 'default', None, True)
314+
e = EventInputEvaluation(timestamp, mc, flag.key, flag, 1, 'value', None, 'default', None, False)
316315
ep.send_event(e)
317316

318317
output = flush_and_get_events(ep)
319-
assert len(output) == 3
318+
assert len(output) == 2
320319
check_index_event(output[0], e, c.to_dict()) # Should only contain non-anon context
321-
check_feature_event(output[1], e)
322-
check_summary_event(output[2])
320+
check_summary_event(output[1])
323321

324322

325323
def test_individual_feature_event_is_ignored_for_0_sampling_ratio():
@@ -328,9 +326,8 @@ def test_individual_feature_event_is_ignored_for_0_sampling_ratio():
328326
ep.send_event(e)
329327

330328
output = flush_and_get_events(ep)
331-
assert len(output) == 2
332-
check_index_event(output[0], e)
333-
check_summary_event(output[1])
329+
assert len(output) == 1
330+
check_summary_event(output[0])
334331

335332

336333
def test_exclude_can_keep_feature_event_from_summary():
@@ -339,54 +336,48 @@ def test_exclude_can_keep_feature_event_from_summary():
339336
ep.send_event(e)
340337

341338
output = flush_and_get_events(ep)
342-
assert len(output) == 2
343-
check_index_event(output[0], e)
344-
check_feature_event(output[1], e)
339+
assert len(output) == 1
340+
check_feature_event(output[0], e)
345341

346342

347343
def test_context_is_filtered_in_index_event():
348344
with DefaultTestProcessor(all_attributes_private=True) as ep:
349345
formatter = EventContextFormatter(True, [])
350-
e = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value', None, 'default', None, True)
346+
e = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value', None, 'default', None, False)
351347
ep.send_event(e)
352348

353349
output = flush_and_get_events(ep)
354-
assert len(output) == 3
350+
assert len(output) == 2
355351
check_index_event(output[0], e, formatter.format_context(context))
356-
check_feature_event(output[1], e, formatter.format_context(context))
357-
check_summary_event(output[2])
352+
check_summary_event(output[1])
358353

359354

360355
def test_two_events_for_same_context_only_produce_one_index_event():
361356
with DefaultTestProcessor(context_keys_flush_interval=300) as ep:
362-
e0 = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value1', None, 'default', None, True)
363-
e1 = EventInputEvaluation(timestamp, context, flag.key, flag, 2, 'value2', None, 'default', None, True)
357+
e0 = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value1', None, 'default', None, False)
358+
e1 = EventInputEvaluation(timestamp, context, flag.key, flag, 2, 'value2', None, 'default', None, False)
364359
ep.send_event(e0)
365360
ep.send_event(e1)
366361

367362
output = flush_and_get_events(ep)
368-
assert len(output) == 4
363+
assert len(output) == 2
369364
check_index_event(output[0], e0)
370-
check_feature_event(output[1], e0)
371-
check_feature_event(output[2], e1)
372-
check_summary_event(output[3])
365+
check_summary_event(output[1])
373366

374367

375368
def test_new_index_event_is_added_if_context_cache_has_been_cleared():
376369
with DefaultTestProcessor(context_keys_flush_interval=0.1) as ep:
377-
e0 = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value1', None, 'default', None, True)
378-
e1 = EventInputEvaluation(timestamp, context, flag.key, flag, 2, 'value2', None, 'default', None, True)
370+
e0 = EventInputEvaluation(timestamp, context, flag.key, flag, 1, 'value1', None, 'default', None, False)
371+
e1 = EventInputEvaluation(timestamp, context, flag.key, flag, 2, 'value2', None, 'default', None, False)
379372
ep.send_event(e0)
380373
time.sleep(0.2)
381374
ep.send_event(e1)
382375

383376
output = flush_and_get_events(ep)
384-
assert len(output) == 5
377+
assert len(output) == 3
385378
check_index_event(output[0], e0)
386-
check_feature_event(output[1], e0)
387-
check_index_event(output[2], e1)
388-
check_feature_event(output[3], e1)
389-
check_summary_event(output[4])
379+
check_index_event(output[1], e1)
380+
check_summary_event(output[2])
390381

391382

392383
def test_event_kind_is_debug_if_flag_is_temporarily_in_debug_mode():
@@ -397,10 +388,9 @@ def test_event_kind_is_debug_if_flag_is_temporarily_in_debug_mode():
397388
ep.send_event(e)
398389

399390
output = flush_and_get_events(ep)
400-
assert len(output) == 3
401-
check_index_event(output[0], e)
402-
check_debug_event(output[1], e)
403-
check_summary_event(output[2])
391+
assert len(output) == 2
392+
check_debug_event(output[0], e)
393+
check_summary_event(output[1])
404394

405395

406396
def test_event_can_be_both_tracked_and_debugged():
@@ -411,11 +401,10 @@ def test_event_can_be_both_tracked_and_debugged():
411401
ep.send_event(e)
412402

413403
output = flush_and_get_events(ep)
414-
assert len(output) == 4
415-
check_index_event(output[0], e)
416-
check_feature_event(output[1], e)
417-
check_debug_event(output[2], e)
418-
check_summary_event(output[3])
404+
assert len(output) == 3
405+
check_feature_event(output[0], e)
406+
check_debug_event(output[1], e)
407+
check_summary_event(output[2])
419408

420409

421410
def test_debug_event_can_be_disabled_with_sampling_ratio():
@@ -426,9 +415,8 @@ def test_debug_event_can_be_disabled_with_sampling_ratio():
426415
ep.send_event(e)
427416

428417
output = flush_and_get_events(ep)
429-
assert len(output) == 2
430-
check_index_event(output[0], e)
431-
check_summary_event(output[1])
418+
assert len(output) == 1
419+
check_summary_event(output[0])
432420

433421

434422
def test_debug_mode_does_not_expire_if_both_client_time_and_server_time_are_before_expiration_time():
@@ -450,10 +438,9 @@ def test_debug_mode_does_not_expire_if_both_client_time_and_server_time_are_befo
450438

451439
# Should get a summary event only, not a full feature event
452440
output = flush_and_get_events(ep)
453-
assert len(output) == 3
454-
check_index_event(output[0], e)
455-
check_debug_event(output[1], e)
456-
check_summary_event(output[2])
441+
assert len(output) == 2
442+
check_debug_event(output[0], e)
443+
check_summary_event(output[1])
457444

458445

459446
def test_debug_mode_expires_based_on_client_time_if_client_time_is_later_than_server_time():

0 commit comments

Comments
 (0)