Skip to content

Conversation

@prestonvasquez
Copy link
Member

@prestonvasquez prestonvasquez commented Nov 8, 2025

GODRIVER-3659

Summary

This PR implements event filtering for awaitMinPoolSizeMS in the unified test runner, as specified in the unified test format specification.

A naive implementation would clear specific event arrays after initialization:

client.pooled = nil
client.serverDescriptionChanged = nil
client.serverHeartbeatStartedEvent = nil
// ... clear each SDAM event type

However, if we add a new CMAP or SDAM event type in the future, we must remember to update this clearing block. Forgetting to do so means initialization events leak into test assertions, causing false failures.

Additionally, the clientEntity requires that the following fields not be reset:

	// These should not be changed after the clientEntity is initialized
	observedEvents                      map[monitoringEventType]struct{}
	storedEvents                        map[monitoringEventType][]string
	eventsCount                         map[monitoringEventType]int32
	serverDescriptionChangedEventsCount map[serverDescriptionChangedEventInfo]int32

This guarantee would have to be removed with the naive approach, at least for eventsCount.

The eventSequencer assigns a monotonically increasing sequence number to each CMAP and SDAM event as it's recorded. After pool initialization completes, we capture the current sequence as a cutoff. When verifying test expectations, we filter out any events with sequence numbers at or below the cutoff. This approach is future-proof because new event types automatically participate in filtering as long as they call the appropriate recording method in their event processor.

Background & Motivation

PR #2196 added support for awaitMinPoolSizeMS to the unified test runner, but was merged before the specification PR mongodb/specifications#1834 was finalized. As a result, the initial implementation used a simplified approach that doesn't match the final specification requirements.

Per the spec, when awaitMinPoolSizeMS is specified:

Any CMAP and SDAM event/log listeners configured on the client should ignore any events that occur before the pool is being populated.

Copilot AI review requested due to automatic review settings November 8, 2025 00:08
@prestonvasquez prestonvasquez requested a review from a team as a code owner November 8, 2025 00:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements event filtering for awaitMinPoolSizeMS in the unified test runner to comply with the specification requirement that CMAP and SDAM events occurring during connection pool initialization should be ignored. The implementation uses a sequence-based filtering approach where events are assigned monotonically increasing sequence numbers, and a cutoff is set after pool initialization completes.

Key Changes:

  • Replaced boolean awaitMinPoolSize field with awaitMinPoolSizeMS integer field to specify timeout duration
  • Introduced eventSequencer to track event ordering via sequence numbers and filter events below a cutoff threshold
  • Modified event processing functions to record sequence numbers for all CMAP and SDAM events

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
internal/integration/unified/entity.go Updated entityOptions to use awaitMinPoolSizeMS with timeout duration instead of boolean flag
internal/integration/unified/client_entity.go Added eventSequencer type and filtering logic; updated awaitMinimumPoolSize to accept timeout parameter and set cutoff after pool initialization
internal/integration/unified/event_verification.go Modified event verification functions to use filterEventsBySeq for filtering CMAP and SDAM events
internal/integration/unified/client_entity_test.go Added comprehensive unit tests for eventSequencer functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mongodb-drivers-pr-bot
Copy link
Contributor

mongodb-drivers-pr-bot bot commented Nov 8, 2025

🧪 Performance Results

Commit SHA: 72051d0

The following benchmark tests for version 692dc954c8079f000778f93b had statistically significant changes (i.e., |z-score| > 1.96):

Benchmark Measurement % Change Patch Value Stable Region H-Score Z-Score
BenchmarkBSONFullDocumentEncoding ops_per_second_min -47.6506 1494.1631 Avg: 2854.2107
Med: 2822.3354
Stdev: 555.4876
0.7872 -2.4484
BenchmarkBSONDeepDocumentEncoding total_mem_allocs 7.2710 261733.0000 Avg: 243992.4286
Med: 241874.5000
Stdev: 8312.0071
0.7438 2.1343
BenchmarkBSONDeepDocumentEncoding total_bytes_allocated 7.1254 186157960.0000 Avg: 173775797.7143
Med: 172294916.0000
Stdev: 5813555.4592
0.7433 2.1299
BenchmarkBSONDeepDocumentEncoding ops_per_second_max 6.7180 81129.3201 Avg: 76022.1212
Med: 75346.7180
Stdev: 2421.0878
0.7454 2.1095
BenchmarkBSONFullDocumentEncoding ops_per_second_med 3.0790 47494.6569 Avg: 46075.9670
Med: 46099.9447
Stdev: 525.4386
0.7957 2.7000
BenchmarkBSONFullDocumentEncoding ns_per_op -2.9085 22696.0000 Avg: 23375.8824
Med: 23493.0000
Stdev: 322.0126
0.7421 -2.1114
BenchmarkBSONFullDocumentEncoding ops_per_second_max 2.7432 48778.1084 Avg: 47475.7748
Med: 47415.8369
Stdev: 515.4858
0.7818 2.5264
BenchmarkMultiFindMany allocated_bytes_per_op -0.3196 1625.0000 Avg: 1630.2105
Med: 1631.0000
Stdev: 2.6197
0.7171 -1.9890
BenchmarkBSONFullDocumentDecoding allocated_bytes_per_op 0.0365 25331.0000 Avg: 25321.7647
Med: 25321.0000
Stdev: 3.5094
0.8614 2.6316

For a comprehensive view of all microbenchmark results for this PR's commit, please check out the Evergreen perf task for this patch.

@mongodb-drivers-pr-bot
Copy link
Contributor

API Change Report

No changes found!

@prestonvasquez prestonvasquez force-pushed the ci/godriver-3659-await-min-pool-size-in-ust-new branch from acbd1d0 to 4110738 Compare November 10, 2025 23:49
@prestonvasquez prestonvasquez added the review-priority-low Low Priority PR for Review: within 3 business days label Nov 17, 2025
return fmt.Errorf("timed out waiting for client to reach minPoolSize")
case <-ticker.C:
if uint64(entity.getEventCount(connectionReadyEvent)) >= minPoolSize {
entity.eventSequencer.setCutoff()
Copy link
Member Author

@prestonvasquez prestonvasquez Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative to this is to re-initialize the event containers here. While this is a much simpler solution, it could be buggy depending if we need to re-initialize event logic for some other reason somewhere else. The eventSequencer future-proofs this issue.

@prestonvasquez
Copy link
Member Author

Closing to focus on a simpler solution for now.

Comment on lines +71 to +75
func (es *eventSequencer) recordEvent(eventType monitoringEventType) {
next := es.counter.Add(1)

es.mu.Lock()
es.seqByEventType[eventType] = append(es.seqByEventType[eventType], next)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of storing every sequence number per event type, can we only store the index when the cutoff happened?

E.g.

func (es *eventSequencer) recordEvent(...) {
	es.mu.Lock()
	defer es.mu.Unlock()

	if !es.isCutoff {
		es.cutoffByEventType[eventType]++
	}
}

func (es *eventSequencer) setCutoff() {
	es.mu.Lock()
	defer es.mu.Unlock()
	es.isCutoff = true
}

That would also simplify filterEventsBySeq because you just get a subslice index:

func filterEventsBySeq[T any](...) []T {
	// ...
	cutIdx := c.eventSequencer.cutoffByEventType[eventType]
	return localEvents[cutIdx:]
}

Copy link
Member Author

@prestonvasquez prestonvasquez Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this approach is simpler, it only supports a single cutoff per test. If the unified spec adds scenarios with multiple cutoffs (e.g., awaitMinPoolSizeMS (implicit cutoff) -> run X -> cutoff at N (either implicit or explicit) -> run Y), we’d have to refactor back to the original design.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement review-priority-low Low Priority PR for Review: within 3 business days

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants