Skip to content

Commit b88cd3f

Browse files
authored
performance (#50)
1 parent 401b59b commit b88cd3f

2 files changed

Lines changed: 35 additions & 8 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CREATE UNIQUE INDEX IF NOT EXISTS idx_blobs_ns_height_index ON blobs(namespace, height, blob_index);
2+
DROP INDEX IF EXISTS idx_blobs_ns_height;

pkg/store/sqlite.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ func configureSQLite(ctx context.Context, db *sql.DB) error {
8989
if _, err := db.ExecContext(ctx, "PRAGMA foreign_keys=ON"); err != nil {
9090
return fmt.Errorf("set foreign_keys: %w", err)
9191
}
92+
// NORMAL is crash-safe with WAL and avoids an extra fsync per commit.
93+
if _, err := db.ExecContext(ctx, "PRAGMA synchronous=NORMAL"); err != nil {
94+
return fmt.Errorf("set synchronous: %w", err)
95+
}
96+
// 64 MB page cache (negative value = KiB).
97+
if _, err := db.ExecContext(ctx, "PRAGMA cache_size=-65536"); err != nil {
98+
return fmt.Errorf("set cache_size: %w", err)
99+
}
100+
// Keep temp tables and sort spills in memory.
101+
if _, err := db.ExecContext(ctx, "PRAGMA temp_store=MEMORY"); err != nil {
102+
return fmt.Errorf("set temp_store: %w", err)
103+
}
92104
return nil
93105
}
94106

@@ -102,6 +114,7 @@ type migrationStep struct {
102114
var allMigrations = []migrationStep{
103115
{version: 1, file: "migrations/001_init.sql"},
104116
{version: 2, file: "migrations/002_commitment_index.sql"},
117+
{version: 3, file: "migrations/003_blob_index_unique.sql"},
105118
}
106119

107120
func (s *SQLiteStore) migrate() error {
@@ -170,14 +183,23 @@ func (s *SQLiteStore) PutBlobs(ctx context.Context, blobs []types.Blob) error {
170183

171184
for i := range blobs {
172185
b := &blobs[i]
173-
if err := ensureSQLiteBlobInvariant(ctx, tx, b); err != nil {
174-
return err
175-
}
176-
if _, err := stmt.ExecContext(ctx,
186+
res, err := stmt.ExecContext(ctx,
177187
b.Height, b.Namespace[:], b.Commitment, b.Data, b.ShareVersion, b.Signer, b.Index,
178-
); err != nil {
188+
)
189+
if err != nil {
179190
return fmt.Errorf("insert blob at height %d index %d: %w", b.Height, b.Index, err)
180191
}
192+
n, err := res.RowsAffected()
193+
if err != nil {
194+
return fmt.Errorf("rows affected at height %d index %d: %w", b.Height, b.Index, err)
195+
}
196+
if n == 0 {
197+
// INSERT OR IGNORE skipped this row — a unique constraint matched.
198+
// Verify it is an idempotent re-insert, not a data conflict.
199+
if err := verifyBlobNotConflicting(ctx, tx, b); err != nil {
200+
return err
201+
}
202+
}
181203
}
182204

183205
return tx.Commit()
@@ -367,12 +389,15 @@ func scanBlobRow(rows *sql.Rows) (types.Blob, error) {
367389
return b, nil
368390
}
369391

370-
func ensureSQLiteBlobInvariant(ctx context.Context, tx *sql.Tx, b *types.Blob) error {
371-
existingByIndex, err := queryBlobByIndex(ctx, tx, b.Namespace, b.Height, b.Index)
392+
// verifyBlobNotConflicting is called only when INSERT OR IGNORE skipped a row.
393+
// It distinguishes an idempotent re-insert (same data) from a true conflict
394+
// (different data at the same position or commitment).
395+
func verifyBlobNotConflicting(ctx context.Context, tx *sql.Tx, b *types.Blob) error {
396+
existing, err := queryBlobByIndex(ctx, tx, b.Namespace, b.Height, b.Index)
372397
if err != nil {
373398
return err
374399
}
375-
if existingByIndex != nil && !sameBlob(existingByIndex, b) {
400+
if existing != nil && !sameBlob(existing, b) {
376401
return fmt.Errorf("blob conflict at height %d namespace %s index %d", b.Height, b.Namespace, b.Index)
377402
}
378403

0 commit comments

Comments
 (0)