Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rqlite for openSUSE:Factory checked in at 2026-01-13 21:23:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rqlite (Old) and /work/SRC/openSUSE:Factory/.rqlite.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rqlite" Tue Jan 13 21:23:44 2026 rev:40 rq:1326495 version:9.3.14 Changes: -------- --- /work/SRC/openSUSE:Factory/rqlite/rqlite.changes 2026-01-08 15:28:28.791685656 +0100 +++ /work/SRC/openSUSE:Factory/.rqlite.new.1928/rqlite.changes 2026-01-13 21:24:10.934232111 +0100 @@ -1,0 +2,8 @@ +Sat Jan 10 08:28:49 UTC 2026 - Andreas Stieger <[email protected]> + +- Update to version 9.3.14: + * Increase WAL Checkpoint busy timeout to 250ms + * Implement a must-checkpoint-WAL after multiple failures policy + * fix SQL comment handling in CLI + +------------------------------------------------------------------- Old: ---- rqlite-9.3.11.tar.xz New: ---- rqlite-9.3.14.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rqlite.spec ++++++ --- /var/tmp/diff_new_pack.fYB0ma/_old 2026-01-13 21:24:11.694263480 +0100 +++ /var/tmp/diff_new_pack.fYB0ma/_new 2026-01-13 21:24:11.694263480 +0100 @@ -17,7 +17,7 @@ Name: rqlite -Version: 9.3.11 +Version: 9.3.14 Release: 0 Summary: Distributed relational database built on SQLite License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.fYB0ma/_old 2026-01-13 21:24:11.746265627 +0100 +++ /var/tmp/diff_new_pack.fYB0ma/_new 2026-01-13 21:24:11.754265957 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/rqlite/rqlite.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v9.3.11</param> + <param name="revision">v9.3.14</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.fYB0ma/_old 2026-01-13 21:24:11.786267278 +0100 +++ /var/tmp/diff_new_pack.fYB0ma/_new 2026-01-13 21:24:11.790267443 +0100 @@ -1,7 +1,7 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/rqlite/rqlite.git</param> - <param name="changesrevision">9589c3caa5da939fb3e2bbb143e0b06b301ac1dd</param> + <param name="changesrevision">369be06aee046ad4248b3d70f169ca8b37188b6a</param> </service> </servicedata> (No newline at EOF) ++++++ rqlite-9.3.11.tar.xz -> rqlite-9.3.14.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.11/CHANGELOG.md new/rqlite-9.3.14/CHANGELOG.md --- old/rqlite-9.3.11/CHANGELOG.md 2026-01-07 05:16:04.000000000 +0100 +++ new/rqlite-9.3.14/CHANGELOG.md 2026-01-09 08:01:54.000000000 +0100 @@ -1,3 +1,16 @@ +## v9.3.14 (January 9th 2025) +### Implementation changes and bug fixes +- [PR #2444](https://github.com/rqlite/rqlite/pull/2444): Increase WAL Checkpoint busy timeout to 250ms. +- [PR #2445](https://github.com/rqlite/rqlite/pull/2445): Implement a must-checkpoint-WAL after multiple failures policy. + +## v9.3.13 (January 8th 2026) +### Implementation changes and bug fixes +- [PR #2443](https://github.com/rqlite/rqlite/pull/2443): Don't scan for rows if zero columns returned by query. Fixes issue [#2441](https://github.com/rqlite/rqlite/issues/2441). + +## v9.3.12 (January 8th 2026) +### Implementation changes and bug fixes +- [PR #2442](https://github.com/rqlite/rqlite/pull/2442): Remove inadvertently committed debug statement. + ## v9.3.11 (January 6th 2026) ### Implementation changes and bug fixes - [PR #2438](https://github.com/rqlite/rqlite/pull/2438): Correctly handle `EXPLAIN QUERY PLAN` for mutations. Fixes issue [#2433](https://github.com/rqlite/rqlite/issues/2433). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.11/db/db.go new/rqlite-9.3.14/db/db.go --- old/rqlite-9.3.11/db/db.go 2026-01-07 05:16:04.000000000 +0100 +++ new/rqlite-9.3.14/db/db.go 2026-01-09 08:01:54.000000000 +0100 @@ -25,11 +25,12 @@ ) const ( - SQLiteHeaderSize = 32 - bkDelay = 250 - durToOpenLog = 2 * time.Second - OptimizeDefault = 0xFFFE - OptimizeAll = 0x10002 + SQLiteHeaderSize = 32 + bkDelay = 250 * time.Millisecond + checkpointBusyTimeout = 250 * time.Millisecond + durToOpenLog = 2 * time.Second + OptimizeDefault = 0xFFFE + OptimizeAll = 0x10002 ) const ( @@ -659,7 +660,7 @@ // Checkpoint checkpoints the WAL file. If the WAL file is not enabled, this // function is a no-op. func (db *DB) Checkpoint(mode CheckpointMode) (*CheckpointMeta, error) { - return db.CheckpointWithTimeout(mode, 1000) + return db.CheckpointWithTimeout(mode, checkpointBusyTimeout) } // CheckpointWithTimeout performs a WAL checkpoint. If the checkpoint does not @@ -1238,7 +1239,7 @@ dest := make([]any, len(columns)) ptrs := make([]any, len(dest)) - for rs.Next() { + for rs.Next() && (len(columns) > 0 || forceStall) { dest := dest[:] ptrs := ptrs[:] for i := range ptrs { @@ -1741,7 +1742,7 @@ break } stats.Add(numBackupSleeps, 1) - time.Sleep(bkDelay * time.Millisecond) + time.Sleep(bkDelay) } return bk.Finish() } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.11/db/db_common_test.go new/rqlite-9.3.14/db/db_common_test.go --- old/rqlite-9.3.11/db/db_common_test.go 2026-01-07 05:16:04.000000000 +0100 +++ new/rqlite-9.3.14/db/db_common_test.go 2026-01-09 08:01:54.000000000 +0100 @@ -106,6 +106,70 @@ } } +func Test_SQL_Comments(t *testing.T) { + db, path := mustCreateOnDiskDatabaseWAL() + defer os.Remove(path) + defer db.Close() + + for _, tt := range []struct { + name string + sql string + exp string + }{ + { + name: "single line comment", + sql: "-- This is a comment\nCREATE TABLE foo (id INTEGER NOT NULL PRIMARY KEY, name TEXT)", + exp: `[{}]`, + }, + { + name: "multi-line comment", + sql: "/* This is a\nmulti-line comment */\nCREATE TABLE bar (id INTEGER NOT NULL PRIMARY KEY, name TEXT)", + exp: `[{}]`, + }, + } { + r, err := db.ExecuteStringStmt(tt.sql) + if err != nil { + t.Fatalf("failed to create table with %s: %s", tt.name, err.Error()) + } + if exp, got := tt.exp, asJSON(r); exp != got { + t.Fatalf("unexpected results for query with %s, expected %s, got %s", tt.name, exp, got) + } + } + + for _, tt := range []struct { + name string + sql string + exp string + }{ + { + name: "comment only", + sql: "-----", + exp: `[{}]`, + }, + { + name: "multi-line comment only", + sql: "/* This is a\nmulti-line comment */", + exp: `[{}]`, + }, + } { + r, err := db.QueryStringStmt(tt.sql) + if err != nil { + t.Fatalf("failed to query with comment %s: %s", tt.name, err.Error()) + } + if exp, got := tt.exp, asJSON(r); exp != got { + t.Fatalf("unexpected results for query with %s, expected %s, got %s", tt.name, exp, got) + } + } + + req, err := db.RequestStringStmts([]string{"-----", "SELECT * FROM qux"}) + if err != nil { + t.Fatalf("failed to request empty statements: %s", err.Error()) + } + if exp, got := `[{},{"error":"no such table: qux"}]`, asJSON(req); exp != got { + t.Fatalf(`unexpected results for request exp: %s got: %s`, exp, got) + } +} + func Test_DB_ExplainSelect(t *testing.T) { db, path := mustCreateOnDiskDatabaseWAL() defer os.Remove(path) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.11/http/service.go new/rqlite-9.3.14/http/service.go --- old/rqlite-9.3.11/http/service.go 2026-01-07 05:16:04.000000000 +0100 +++ new/rqlite-9.3.14/http/service.go 2026-01-09 08:01:54.000000000 +0100 @@ -1444,7 +1444,6 @@ stats.Add(numQueryStmtsRx, int64(len(queries))) if !qp.NoParse() { - fmt.Println("Processing queries:", queries) if err := sql.Process(queries, qp.NoRewriteRandom(), !qp.NoRewriteTime()); err != nil { http.Error(w, fmt.Sprintf("SQL rewrite: %s", err.Error()), http.StatusInternalServerError) return diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.11/store/store.go new/rqlite-9.3.14/store/store.go --- old/rqlite-9.3.11/store/store.go 2026-01-07 05:16:04.000000000 +0100 +++ new/rqlite-9.3.14/store/store.go 2026-01-09 08:01:54.000000000 +0100 @@ -138,6 +138,7 @@ raftLogCacheSize = 512 trailingScale = 1.25 observerChanLen = 50 + maxFailedSnapshotsInRow = 5 baseVacuumTimeKey = "rqlite_base_vacuum" lastVacuumTimeKey = "rqlite_last_vacuum" @@ -344,9 +345,10 @@ snapshotWClose chan struct{} snapshotWDone chan struct{} - // Snapshotting synchronization - snapshotSync *rsync.SyncChannels - snapshotCAS *rsync.CheckAndSet + // Snapshotting synchronization and and management + snapshotSync *rsync.SyncChannels + snapshotCAS *rsync.CheckAndSet + numFailedSnapshotsInRow int // Latest log entry index actually reflected by the FSM. Due to Raft code // these values are not updated automatically after a Snapshot-restore. @@ -2533,22 +2535,11 @@ var walTmpFD *os.File if fullNeeded { chkStartTime := time.Now() - meta, err := s.db.Checkpoint(sql.CheckpointTruncate) - if err != nil { + if err := s.checkpointWAL(); err != nil { stats.Add(numFullCheckpointFailed, 1) - return nil, fmt.Errorf("snapshot can't complete due to FULL Snapshot checkpoint error (will retry): %s", + return nil, fmt.Errorf("full snapshot can't complete due to WAL checkpoint error (will retry): %s", err.Error()) } - if !meta.Success() { - if meta.Moved < meta.Pages { - stats.Add(numWALCheckpointIncomplete, 1) - return nil, fmt.Errorf("snapshot can't complete due to FULL Snapshot checkpoint incomplete (will retry): %s)", - meta.String()) - } - s.logger.Printf("full Snapshot checkpoint moved %d/%d pages, but did not truncate WAL, forcing truncate", - meta.Moved, meta.Pages) - s.mustTruncateCheckpoint() - } stats.Get(snapshotCreateChkTruncateDuration).(*expvar.Int).Set(time.Since(chkStartTime).Milliseconds()) dbFD, err := os.Open(s.db.Path()) if err != nil { @@ -2605,22 +2596,11 @@ return nil, err } chkTStartTime := time.Now() - meta, err := s.db.Checkpoint(sql.CheckpointTruncate) - if err != nil { + if err := s.checkpointWAL(); err != nil { stats.Add(numWALCheckpointTruncateFailed, 1) - return nil, fmt.Errorf("snapshot can't complete due to WAL checkpoint error (will retry): %s", + return nil, fmt.Errorf("incremental snapshot can't complete due to WAL checkpoint error (will retry): %s", err.Error()) } - if !meta.Success() { - if meta.Moved < meta.Pages { - stats.Add(numWALCheckpointIncomplete, 1) - return nil, fmt.Errorf("snapshot can't complete due to Snapshot checkpoint incomplete (will retry %s)", - meta.String()) - } - s.logger.Printf("incremental Snapshot checkpoint moved %d/%d pages, but did not truncate WAL, forcing truncate", - meta.Moved, meta.Pages) - s.mustTruncateCheckpoint() - } stats.Get(snapshotCreateChkTruncateDuration).(*expvar.Int).Set(time.Since(chkTStartTime).Milliseconds()) stats.Get(snapshotPrecompactWALSize).(*expvar.Int).Set(walSzPre) stats.Get(snapshotWALSize).(*expvar.Int).Set(walSzPost) @@ -2923,6 +2903,46 @@ return closeCh, doneCh } +// checkpointWAL performs a checkpoint of the WAL, truncating it. If it returns an error +// the checkpoint operation can be retried at the caller's discretion. If this function +// encounters an error such that the checkpoint must be retried, it will automatically do +// that until it is successful (or a timeout fires). +// +// This function also implements the policy that if a certain number of checkpoint attempts +// fail in a row, it will loop until is successful. +func (s *Store) checkpointWAL() (retErr error) { + defer func() { + if retErr != nil { + s.numFailedSnapshotsInRow++ + if s.numFailedSnapshotsInRow == maxFailedSnapshotsInRow { + s.logger.Printf("too many failed snapshots in a row (%d), forcing WAL checkpoint truncate", + s.numFailedSnapshotsInRow) + s.mustTruncateCheckpoint() + s.numFailedSnapshotsInRow = 0 + retErr = nil + } + } else { + s.numFailedSnapshotsInRow = 0 + } + }() + + meta, err := s.db.Checkpoint(sql.CheckpointTruncate) + if err != nil { + return err + } + if !meta.Success() { + if meta.Pages == meta.Moved { + s.logger.Printf("checkpoint moved %d/%d pages, but did not truncate WAL, forcing truncate", + meta.Moved, meta.Pages) + s.mustTruncateCheckpoint() + return nil + } + stats.Add(numWALCheckpointIncomplete, 1) + return fmt.Errorf("checkpoint incomplete: %s", meta.String()) + } + return nil +} + // mustTruncateCheckpoint truncates the checkpointed WAL, retrying until successful or // timing out. // @@ -2932,8 +2952,8 @@ // We do this by blocking all readers (writes are already blocked). This handling is due to // research into SQLite and not seen as of yet. // -// Finally, we could still panic here if we timeout trying to truncate. This could happen if -// a reader external to rqlite just won't let go. +// Finally, we could still timeout here while trying to truncate. This could happen if a +// reader external to rqlite just won't let go. func (s *Store) mustTruncateCheckpoint() { startT := time.Now() defer func() { @@ -2954,7 +2974,10 @@ return } case <-time.After(mustWALCheckpointTimeout): - s.logger.Fatal("timed out trying to truncate checkpointed WAL - aborting") + msg := fmt.Sprintf("timed out trying to truncate checkpoint WAL after %s,"+ + " probably due to external long-running read - aborting", + mustWALCheckpointTimeout) + s.logger.Fatal(msg) } } } ++++++ vendor.tar.xz ++++++
