Skip to content

[BUG-CONC-5] Check-Then-Act Race in getDbWriterForTable - Non-Atomic Map Operations #1263

@minguyen9988

Description

@minguyen9988

Bug Description

Severity: HIGH
Component: ClickHouseBatchRunnable
Version: 2.9.1

Problem

The getDbWriterForTable() method uses a check-then-act pattern on topicToDbWriterMap (a plain HashMap) that is not atomic. In a multi-threaded environment, the containsKey() check (line 515) and subsequent get()/remove() (lines 520, 522) are separate operations that can be interleaved by other threads.

Affected Code

File: sink-connector/src/main/java/com/altinity/clickhouse/sink/connector/executor/ClickHouseBatchRunnable.java

// Lines 510-533: Non-atomic check-then-act pattern
public DbWriter getDbWriterForTable(String topicName, ...) {
    DbWriter writer = null;
    if (this.topicToDbWriterMap.containsKey(topicName)) {  // Line 515: CHECK
        // ... cache invalidation check ...
        this.topicToDbWriterMap.remove(topicName);          // Line 520: ACT (not atomic with CHECK)
        writer = this.topicToDbWriterMap.get(topicName);    // Line 522: ACT (not atomic with CHECK)
        return writer;
    }
    writer = new DbWriter(...);                             // Line 526: Multiple threads may create
    this.topicToDbWriterMap.put(topicName, writer);          // Line 531: Last write wins
    return writer;
}

Impact

Proposed Fix

Use ConcurrentHashMap.computeIfAbsent() for atomic check-then-create:

// Thread-safe atomic creation
private final Map<String, DbWriter> topicToDbWriterMap = new ConcurrentHashMap<>();

public DbWriter getDbWriterForTable(String topicName, ...) {
    // Handle cache invalidation
    String fqtn = databaseName + "." + tableName;
    if (CacheInvalidationManager.getInstance().shouldInvalidate(fqtn)) {
        topicToDbWriterMap.remove(topicName);
    }
    
    return topicToDbWriterMap.computeIfAbsent(topicName, key ->
        new DbWriter(dbCredentials.getHostName(), ...));
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions