Skip to content

Commit fbe11a4

Browse files
EDsCODEclaude
andcommitted
Use double-checked locking for DuckLake attachment
Move the "is DuckLake attached?" check outside the mutex so that subsequent connections don't block waiting for the mutex. The mutex is only acquired when DuckLake actually needs to be attached (first connection only). This prevents connection backups when many clients connect simultaneously after the first connection has already attached DuckLake. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c466abb commit fbe11a4

File tree

1 file changed

+17
-9
lines changed

1 file changed

+17
-9
lines changed

server/server.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,27 @@ func (s *Server) attachDuckLake(db *sql.DB) error {
330330
return nil // DuckLake not configured
331331
}
332332

333-
// Serialize DuckLake attachment to avoid race conditions
334-
// Multiple connections trying to attach simultaneously can cause
333+
// Fast path: check if DuckLake is already attached (no mutex needed)
334+
// This avoids blocking on the mutex for the common case where DuckLake
335+
// was already attached by an earlier connection.
336+
var count int
337+
err := db.QueryRow("SELECT COUNT(*) FROM duckdb_databases() WHERE database_name = 'ducklake'").Scan(&count)
338+
if err == nil && count > 0 {
339+
// Already attached, just set as default
340+
if _, err := db.Exec("USE ducklake"); err != nil {
341+
return fmt.Errorf("failed to set DuckLake as default catalog: %w", err)
342+
}
343+
return nil
344+
}
345+
346+
// Slow path: need to attach DuckLake. Serialize to avoid race conditions
347+
// where multiple connections try to attach simultaneously, causing
335348
// "database with name '__ducklake_metadata_ducklake' already exists" errors
336-
//
337-
// TODO: This mutex can block all incoming connections if DuckLake attachment
338-
// is slow (e.g., network latency to metadata store). Consider adding a timeout
339-
// or moving attachment to happen asynchronously after connection establishment.
340349
s.duckLakeMu.Lock()
341350
defer s.duckLakeMu.Unlock()
342351

343-
// Check if DuckLake catalog is already attached
344-
var count int
345-
err := db.QueryRow("SELECT COUNT(*) FROM duckdb_databases() WHERE database_name = 'ducklake'").Scan(&count)
352+
// Double-check after acquiring mutex (another goroutine may have attached)
353+
err = db.QueryRow("SELECT COUNT(*) FROM duckdb_databases() WHERE database_name = 'ducklake'").Scan(&count)
346354
if err == nil && count > 0 {
347355
// Already attached, just set as default
348356
if _, err := db.Exec("USE ducklake"); err != nil {

0 commit comments

Comments
 (0)