Skip to content

Commit 1541bcc

Browse files
l33tdawgclaude
andcommitted
fix: CometBFT height deserialization and SQLite write contention
CometBFT returns block height as a JSON string (e.g. "6294") but cometCommitResponse.Height was typed as int64, causing unmarshal failures and HTTP 500 on all /v1/memory/submit calls. Fixed with the json:",string" tag. Also added a write mutex to SQLiteStore.RunInTx to serialize write transactions at the Go level, preventing SQLITE_BUSY errors when concurrent DEFERRED transactions both escalate to write locks. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent c4aa186 commit 1541bcc

3 files changed

Lines changed: 8 additions & 2 deletions

File tree

api/rest/handlers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ func TestSubmitMemory(t *testing.T) {
307307
"check_tx": map[string]interface{}{"code": 0, "log": ""},
308308
"tx_result": map[string]interface{}{"code": 0, "data": "", "log": "memory submitted"},
309309
"hash": "ABCDEF1234567890",
310-
"height": 1,
310+
"height": "1",
311311
},
312312
})
313313
}))

api/rest/memory_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ type cometCommitResponse struct {
127127
Log string `json:"log"`
128128
} `json:"tx_result"`
129129
Hash string `json:"hash"`
130-
Height int64 `json:"height"`
130+
Height int64 `json:"height,string"`
131131
} `json:"result"`
132132
Error *struct {
133133
Code int `json:"code"`

internal/store/sqlite.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type SQLiteStore struct {
3838
vault *vault.Vault // nil = no encryption
3939
vaultExpected bool // true = encryption should be active; reject writes if vault nil
4040
decryptWarnOnce sync.Once // gates the one-time decryption failure warning
41+
writeMu sync.Mutex // serializes write transactions to prevent SQLITE_BUSY
4142
}
4243

4344
// encPrefix marks content as encrypted (prepended to base64 ciphertext).
@@ -2473,6 +2474,11 @@ func (s *SQLiteStore) RunInTx(ctx context.Context, fn func(tx OffchainStore) err
24732474
// Already in a transaction — execute directly.
24742475
return fn(s)
24752476
}
2477+
// Serialize write transactions at the Go level to prevent SQLITE_BUSY.
2478+
// SQLite's busy_timeout handles statement-level contention, but concurrent
2479+
// DEFERRED transactions that both escalate to write locks can still fail.
2480+
s.writeMu.Lock()
2481+
defer s.writeMu.Unlock()
24762482
tx, err := s.db.BeginTx(ctx, nil)
24772483
if err != nil {
24782484
return fmt.Errorf("begin tx: %w", err)

0 commit comments

Comments
 (0)