Skip to content

SNOW-2924941: OCSP cache clearer stop() has race condition due to buffered channel #1669

@obs-gh-felixbowing

Description

@obs-gh-felixbowing

Description

The stopOCSPCacheClearing channel is defined as a buffered channel with capacity 2:

https://github.com/snowflakedb/gosnowflake/blob/master/ocsp.go#L109

var stopOCSPCacheClearing = make(chan struct{}, 2)

This causes a race condition in the stop() method:

func (occ *ocspCacheClearerType) stop() {
	occ.mu.Lock()
	running := occ.running
	occ.mu.Unlock()
	if running {
		stopOCSPCacheClearing <- struct{}{}  // Non-blocking, goes into buffer
		<-stopOCSPCacheClearing              // Could immediately read its own value!
	}
}

Problem

With a buffered channel:

  1. The send in stop() puts a value into the buffer (non-blocking since buffer has space)
  2. The receive in stop() could immediately read that same value back
  3. stop() returns without the goroutine ever seeing the stop signal

The goroutine continues running, and the synchronization handshake is broken.

Expected Behavior

The stop() function should block until the cache clearer goroutine has actually stopped.

Suggested Fix

Change the channel to be unbuffered:

var stopOCSPCacheClearing = make(chan struct{})

With an unbuffered channel:

  • The send in stop() blocks until the goroutine receives
  • The goroutine's acknowledgment send blocks until stop() receives

This ensures proper synchronization for the handshake pattern.

Impact

This bug can cause:

  • Goroutine leaks when StopOCSPCacheClearer() is called
  • Resources (ticker) not being released
  • Non-deterministic behavior in tests

Metadata

Metadata

Labels

status-fixed_awaiting_releaseThe issue has been fixed, its PR merged, and now awaiting the next release cycle of the connector.status-triage_doneInitial triage done, will be further handled by the driver team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions