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-03-11 20:52:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rqlite (Old)
 and      /work/SRC/openSUSE:Factory/.rqlite.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rqlite"

Wed Mar 11 20:52:54 2026 rev:46 rq:1338088 version:9.4.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/rqlite/rqlite.changes    2026-03-10 
17:53:45.864666586 +0100
+++ /work/SRC/openSUSE:Factory/.rqlite.new.8177/rqlite.changes  2026-03-11 
20:53:45.778096284 +0100
@@ -1,0 +2,7 @@
+Tue Mar 10 19:12:22 UTC 2026 - Andreas Stieger <[email protected]>
+
+- Update to version 9.4.5:
+  * Rely soley on SQLite busy for WAL truncate
+  * Prevent deadlock situation during raft snapshots
+
+-------------------------------------------------------------------

Old:
----
  rqlite-9.4.3.tar.xz

New:
----
  rqlite-9.4.5.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rqlite.spec ++++++
--- /var/tmp/diff_new_pack.ACYjUd/_old  2026-03-11 20:53:46.654132086 +0100
+++ /var/tmp/diff_new_pack.ACYjUd/_new  2026-03-11 20:53:46.658132250 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           rqlite
-Version:        9.4.3
+Version:        9.4.5
 Release:        0
 Summary:        Distributed relational database built on SQLite
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.ACYjUd/_old  2026-03-11 20:53:46.694133721 +0100
+++ /var/tmp/diff_new_pack.ACYjUd/_new  2026-03-11 20:53:46.702134048 +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.4.3</param>
+    <param name="revision">v9.4.5</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.ACYjUd/_old  2026-03-11 20:53:46.738135519 +0100
+++ /var/tmp/diff_new_pack.ACYjUd/_new  2026-03-11 20:53:46.742135683 +0100
@@ -1,7 +1,7 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">https://github.com/rqlite/rqlite.git</param>
-    <param 
name="changesrevision">379044842cbed8e9ff2b7f3aa69539f2a7494a9b</param>
+    <param 
name="changesrevision">41d7a347ab2db0fa494fc9553b9d9467c343a678</param>
   </service>
 </servicedata>
 (No newline at EOF)

++++++ rqlite-9.4.3.tar.xz -> rqlite-9.4.5.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/CHANGELOG.md 
new/rqlite-9.4.5/CHANGELOG.md
--- old/rqlite-9.4.3/CHANGELOG.md       2026-03-09 15:12:14.000000000 +0100
+++ new/rqlite-9.4.5/CHANGELOG.md       2026-03-10 03:03:14.000000000 +0100
@@ -1,3 +1,15 @@
+## v9.4.4 (March 9th 2026)
+### Implementation changes and bug fixes
+- [PR #2544](https://github.com/rqlite/rqlite/pull/2544): Ensure the 
checkpoint-truncate runs to completion, or exit. Fixes issue 
[#2537](https://github.com/rqlite/rqlite/issues/2537).
+
+## v9.4.3 (March 9th 2026)
+### Implementation changes and bug fixes
+- 
[b94b538](https://github.com/rqlite/rqlite/commit/b94b538451622d1f04bebc19aa580297457f439f):
 Remove `mustTruncate` as it can result in a deadlock. It was introduced in 
v9.3.14, but has been shown to be flawed. See [issue 
#2537](https://github.com/rqlite/rqlite/issues/2537).
+
+## v9.4.2 (March 9th 2026)
+### Implementation changes and bug fixes
+- [PR #2533](https://github.com/rqlite/rqlite/pull/2533): Fix code in `db` 
package which hits an error, but was actually returning `nil`. Fixes issue 
[#2530](https://github.com/rqlite/rqlite/issues/2530).
+
 ## v9.4.1 (February 11th 2026)
 ### Implementation changes and bug fixes
 - [PR #2468](https://github.com/rqlite/rqlite/pull/2468): Add _proxy_ layer 
between HTTP layer and Store.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/db/db.go new/rqlite-9.4.5/db/db.go
--- old/rqlite-9.4.3/db/db.go   2026-03-09 15:12:14.000000000 +0100
+++ new/rqlite-9.4.5/db/db.go   2026-03-10 03:03:14.000000000 +0100
@@ -28,6 +28,7 @@
        SQLiteHeaderSize      = 32
        bkDelay               = 250 * time.Millisecond
        checkpointBusyTimeout = 250 * time.Millisecond
+       checkpointBusyDelay   = 10 * time.Millisecond
        durToOpenLog          = 2 * time.Second
        OptimizeDefault       = 0xFFFE
        OptimizeAll           = 0x10002
@@ -37,7 +38,6 @@
        openDuration              = "open_duration_ms"
        numCheckpoints            = "checkpoints"
        numCheckpointErrors       = "checkpoint_errors"
-       numCheckpointedPages      = "checkpointed_pages"
        numCheckpointedMoves      = "checkpointed_moves"
        checkpointDuration        = "checkpoint_duration_ms"
        numExecutions             = "executions"
@@ -105,7 +105,6 @@
        stats.Add(openDuration, 0)
        stats.Add(numCheckpoints, 0)
        stats.Add(numCheckpointErrors, 0)
-       stats.Add(numCheckpointedPages, 0)
        stats.Add(numCheckpointedMoves, 0)
        stats.Add(checkpointDuration, 0)
        stats.Add(numExecutions, 0)
@@ -664,6 +663,49 @@
        return db.CheckpointWithTimeout(mode, checkpointBusyTimeout)
 }
 
+// CheckpointTruncateWithTimeout performs a WAL checkpoint in TRUNCATE mode.
+// The caller must guarantee that no write transactions will commit for the
+// duration of the call. If all readers release their locks within dur,
+// the WAL is truncated and nil is returned. If readers hold locks for
+// the entire duration, an error is returned.
+func (db *DB) CheckpointTruncateWithTimeout(dur time.Duration) error {
+       rwBt, _, err := db.BusyTimeout()
+       if err != nil {
+               return fmt.Errorf("failed to get busy_timeout: %s", err.Error())
+       }
+       if err := db.SetBusyTimeout(int(dur.Milliseconds()), -1); err != nil {
+               return fmt.Errorf("failed to set busy_timeout: %s", err.Error())
+       }
+       defer func() {
+               if err := db.SetBusyTimeout(rwBt, -1); err != nil {
+                       db.logger.Printf("failed to reset busy_timeout: %s", 
err.Error())
+               }
+       }()
+
+       currMode, err := db.GetSynchronousMode()
+       if err != nil {
+               return fmt.Errorf("failed to get synchronous mode: %s", 
err.Error())
+       }
+       if err := db.SetSynchronousMode(SynchronousFull); err != nil {
+               return fmt.Errorf("failed to set synchronous mode to FULL: %s", 
err.Error())
+       }
+       defer func() {
+               if err := db.SetSynchronousMode(currMode); err != nil {
+                       db.logger.Fatalf("failed to reset synchronous mode to 
%s: %s", currMode, err.Error())
+               }
+       }()
+
+       ok, _, nMoved, err := checkpointDB(db.rwDB, CheckpointTruncate)
+       if err != nil {
+               return fmt.Errorf("error checkpointing WAL: %s", err.Error())
+       }
+       if ok != 0 {
+               return fmt.Errorf("checkpoint did not complete within %s", dur)
+       }
+       stats.Add(numCheckpointedMoves, int64(nMoved))
+       return nil
+}
+
 // CheckpointWithTimeout performs a WAL checkpoint. If the checkpoint does not
 // run to completion within the given duration, an error is returned. If the
 // duration is 0, the busy timeout is not modified before executing the
@@ -716,7 +758,6 @@
        if err != nil {
                return nil, fmt.Errorf("error checkpointing WAL: %s", 
err.Error())
        }
-       stats.Add(numCheckpointedPages, int64(nPages))
        stats.Add(numCheckpointedMoves, int64(nMoved))
        return &CheckpointMeta{
                Code:  ok,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/db/db_checkpoint_test.go 
new/rqlite-9.4.5/db/db_checkpoint_test.go
--- old/rqlite-9.4.3/db/db_checkpoint_test.go   2026-03-09 15:12:14.000000000 
+0100
+++ new/rqlite-9.4.5/db/db_checkpoint_test.go   2026-03-10 03:03:14.000000000 
+0100
@@ -5,6 +5,7 @@
        "context"
        "io"
        "os"
+       "sync"
        "testing"
        "time"
 
@@ -518,6 +519,106 @@
        }
 }
 
+func Test_WALDatabaseCheckpointTruncateOK(t *testing.T) {
+       path := mustTempFile()
+       defer os.Remove(path)
+
+       db, err := Open(path, false, true)
+       if err != nil {
+               t.Fatalf("failed to open database in WAL mode: %s", err.Error())
+       }
+       defer db.Close()
+
+       _, err = db.ExecuteStringStmt(`CREATE TABLE foo (id INTEGER NOT NULL 
PRIMARY KEY, name TEXT)`)
+       if err != nil {
+               t.Fatalf("failed to execute on single node: %s", err.Error())
+       }
+
+       err = db.CheckpointTruncateWithTimeout(100 * time.Millisecond)
+       if err != nil {
+               t.Fatalf("failed to checkpoint database: %s", err.Error())
+       }
+
+       // Confirm that the WAL file is zero bytes long.
+       sz, err := fileSize(db.WALPath())
+       if err != nil {
+               t.Fatalf("failed to get WAL file size: %s", err.Error())
+       }
+       if sz != 0 {
+               t.Fatalf("expected WAL file size to be 0 after checkpoint 
truncate, got %d", sz)
+       }
+
+       // Ensure idempotency by checkpointing again.
+       err = db.CheckpointTruncateWithTimeout(100 * time.Millisecond)
+       if err != nil {
+               t.Fatalf("failed to checkpoint database: %s", err.Error())
+       }
+
+       // Ensure that the database remains in Synchronous=OFF mode.
+       syncMode, err := db.GetSynchronousMode()
+       if err != nil {
+               t.Fatalf("failed to get synchronous mode: %s", err.Error())
+       }
+       if syncMode != SynchronousOff {
+               t.Fatalf("expected synchronous mode to be OFF, got %s", 
syncMode)
+       }
+}
+
+func Test_WALDatabaseCheckpointTruncateFail_Blocked(t *testing.T) {
+       path := mustTempFile()
+       defer os.Remove(path)
+
+       db, err := Open(path, false, true)
+       if err != nil {
+               t.Fatalf("failed to open database in WAL mode: %s", err.Error())
+       }
+       defer db.Close()
+
+       _, err = db.ExecuteStringStmt(`CREATE TABLE foo (id INTEGER NOT NULL 
PRIMARY KEY, name TEXT)`)
+       if err != nil {
+               t.Fatalf("failed to execute on single node: %s", err.Error())
+       }
+       _, err = db.ExecuteStringStmt(`INSERT INTO foo(name) VALUES("alice")`)
+       if err != nil {
+               t.Fatalf("failed to execute INSERT on single node: %s", 
err.Error())
+       }
+
+       // Issue a long-running read that should block the checkpoint.
+       qr := &command.Request{
+               Statements: []*command.Statement{
+                       {
+                               Sql:        "SELECT * FROM foo",
+                               ForceStall: true,
+                       },
+               },
+       }
+       ctx, cancelFunc := context.WithCancel(context.Background())
+       go func() {
+               db.QueryWithContext(ctx, qr, false)
+       }()
+       time.Sleep(2 * time.Second)
+       err = db.CheckpointTruncateWithTimeout(100 * time.Millisecond)
+       if err == nil {
+               t.Fatalf("expected checkpoint to fail due to blocking read")
+       }
+
+       // Now, start the checkpoint again, but this time with a longer timeout.
+       // Kick it off in a goroutine, and then cancel the blocking read after 
a short delay,
+       // and confirm that the checkpoint succeeds.
+       var blockErr error
+       var wg sync.WaitGroup
+       wg.Go(func() {
+               blockErr = db.CheckpointTruncateWithTimeout(5 * time.Second)
+       })
+
+       time.Sleep(2 * time.Second)
+       cancelFunc()
+       wg.Wait()
+       if blockErr != nil {
+               t.Fatalf("expected checkpoint to succeed after blocking read 
was cancelled, got error: %s", blockErr.Error())
+       }
+}
+
 func mustReadBytes(path string) []byte {
        b, err := os.ReadFile(path)
        if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/db/swappable_db.go 
new/rqlite-9.4.5/db/swappable_db.go
--- old/rqlite-9.4.3/db/swappable_db.go 2026-03-09 15:12:14.000000000 +0100
+++ new/rqlite-9.4.5/db/swappable_db.go 2026-03-10 03:03:14.000000000 +0100
@@ -156,11 +156,12 @@
        return s.db.StmtReadOnly(sql)
 }
 
-// Checkpoint calls Checkpoint on the underlying database.
-func (s *SwappableDB) Checkpoint(mode CheckpointMode) (*CheckpointMeta, error) 
{
+// CheckpointTruncateWithTimeout calls CheckpointTruncateWithTimeout on the
+// underlying database.
+func (s *SwappableDB) CheckpointTruncateWithTimeout(timeout time.Duration) 
error {
        s.dbMu.RLock()
        defer s.dbMu.RUnlock()
-       return s.db.Checkpoint(mode)
+       return s.db.CheckpointTruncateWithTimeout(timeout)
 }
 
 // Optimize calls Optimize on the underlying database.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/store/state.go 
new/rqlite-9.4.5/store/state.go
--- old/rqlite-9.4.3/store/state.go     2026-03-09 15:12:14.000000000 +0100
+++ new/rqlite-9.4.5/store/state.go     2026-03-10 03:03:14.000000000 +0100
@@ -210,13 +210,10 @@
 
        // Create a new snapshot, placing the configuration in as if it was
        // committed at index 1.
-       meta, err := db.Checkpoint(sql.CheckpointTruncate)
+       err = db.CheckpointTruncateWithTimeout(truncateTimeout)
        if err != nil {
                return fmt.Errorf("failed to checkpoint database: %s", err)
        }
-       if !meta.Success() {
-               return fmt.Errorf("database checkpoint was not successful: %s", 
meta.String())
-       }
        tmpDBFD, err := os.Open(tmpDBPath)
        if err != nil {
                return fmt.Errorf("failed to open temporary database file: %s", 
err)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/store/store.go 
new/rqlite-9.4.5/store/store.go
--- old/rqlite-9.4.3/store/store.go     2026-03-09 15:12:14.000000000 +0100
+++ new/rqlite-9.4.5/store/store.go     2026-03-10 03:03:14.000000000 +0100
@@ -130,6 +130,7 @@
        leaderWaitDelay           = 100 * time.Millisecond
        appliedWaitDelay          = 100 * time.Millisecond
        commitEquivalenceDelay    = 50 * time.Millisecond
+       truncateTimeout           = 5 * time.Minute
        backupCASTimeout          = 10 * time.Second
        backupCASRetryDelay       = 100 * time.Millisecond
        connectionPoolCount       = 5
@@ -156,11 +157,6 @@
        numWALSnapshotsFailed             = "num_wal_snapshots_failed"
        numSnapshotsFull                  = "num_snapshots_full"
        numSnapshotsIncremental           = "num_snapshots_incremental"
-       numFullCheckpointFailed           = "num_full_checkpoint_failed"
-       numWALCheckpointTruncateFailed    = "num_wal_checkpoint_truncate_failed"
-       numWALCheckpointAllMovedFailed    = 
"num_wal_checkpoint_all_moved_failed"
-       numWALCheckpointIncomplete        = "num_wal_checkpoint_incomplete"
-       numWALMustCheckpoint              = "num_wal_must_checkpoint"
        numAutoVacuums                    = "num_auto_vacuums"
        numAutoVacuumsFailed              = "num_auto_vacuums_failed"
        autoVacuumDuration                = "auto_vacuum_duration"
@@ -226,11 +222,6 @@
        stats.Add(numWALSnapshotsFailed, 0)
        stats.Add(numSnapshotsFull, 0)
        stats.Add(numSnapshotsIncremental, 0)
-       stats.Add(numFullCheckpointFailed, 0)
-       stats.Add(numWALCheckpointTruncateFailed, 0)
-       stats.Add(numWALCheckpointAllMovedFailed, 0)
-       stats.Add(numWALCheckpointIncomplete, 0)
-       stats.Add(numWALMustCheckpoint, 0)
        stats.Add(numAutoVacuums, 0)
        stats.Add(numAutoVacuumsFailed, 0)
        stats.Add(autoVacuumDuration, 0)
@@ -2576,10 +2567,8 @@
        var walTmpFD *os.File
        if fullNeeded {
                chkStartTime := time.Now()
-               if err := s.checkpointWAL(); err != nil {
-                       stats.Add(numFullCheckpointFailed, 1)
-                       return nil, fmt.Errorf("full snapshot can't complete 
due to WAL checkpoint error (will retry): %s",
-                               err.Error())
+               if err := s.db.CheckpointTruncateWithTimeout(truncateTimeout); 
err != nil {
+                       s.logger.Fatalf("failed to checkpoint and truncate 
database for full snapshot: %s", err.Error())
                }
                
stats.Get(snapshotCreateChkTruncateDuration).(*expvar.Int).Set(time.Since(chkStartTime).Milliseconds())
                dbFD, err := os.Open(s.db.Path())
@@ -2642,12 +2631,10 @@
                                return nil, err
                        }
                        chkTStartTime := time.Now()
-                       if err := s.checkpointWAL(); err != nil {
+                       if err := 
s.db.CheckpointTruncateWithTimeout(truncateTimeout); err != nil {
                                walTmpFD.Close()
                                os.Remove(walTmpFD.Name())
-                               stats.Add(numWALCheckpointTruncateFailed, 1)
-                               return nil, fmt.Errorf("incremental snapshot 
can't complete due to WAL checkpoint error (will retry): %s",
-                                       err.Error())
+                               s.logger.Fatalf("failed to checkpoint and 
truncate database for incremental snapshot: %s", err.Error())
                        }
                        
stats.Get(snapshotCreateChkTruncateDuration).(*expvar.Int).Set(time.Since(chkTStartTime).Milliseconds())
                        
stats.Get(snapshotPrecompactWALSize).(*expvar.Int).Set(walSzPre)
@@ -2951,25 +2938,6 @@
        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.
-func (s *Store) checkpointWAL() (retErr error) {
-       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 all pages (%d/%d), 
but failed to truncate WAL",
-                               meta.Moved, meta.Pages)
-                       stats.Add(numWALCheckpointAllMovedFailed, 1)
-               }
-               stats.Add(numWALCheckpointIncomplete, 1)
-               return fmt.Errorf("checkpoint incomplete: %s", meta.String())
-       }
-       return nil
-}
-
 // selfLeaderChange is called when this node detects that its leadership
 // status has changed.
 func (s *Store) selfLeaderChange(leader bool) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-9.4.3/store/store_snapshot_test.go 
new/rqlite-9.4.5/store/store_snapshot_test.go
--- old/rqlite-9.4.3/store/store_snapshot_test.go       2026-03-09 
15:12:14.000000000 +0100
+++ new/rqlite-9.4.5/store/store_snapshot_test.go       2026-03-10 
03:03:14.000000000 +0100
@@ -11,7 +11,6 @@
        "time"
 
        "github.com/rqlite/rqlite/v9/command/proto"
-       "github.com/rqlite/rqlite/v9/db"
        "github.com/rqlite/rqlite/v9/internal/random"
 )
 
@@ -228,69 +227,6 @@
        }
 }
 
-func Test_SingleNode_SnapshotFail_Blocked(t *testing.T) {
-       s, ln := mustNewStore(t)
-       defer ln.Close()
-
-       s.SnapshotThreshold = 8192
-       s.SnapshotInterval = time.Hour
-       s.NoSnapshotOnClose = true
-       if err := s.Open(); err != nil {
-               t.Fatalf("failed to open single-node store: %s", err.Error())
-       }
-       defer s.Close(true)
-       if err := s.Bootstrap(NewServer(s.ID(), s.Addr(), true)); err != nil {
-               t.Fatalf("failed to bootstrap single-node store: %s", 
err.Error())
-       }
-       if _, err := s.WaitForLeader(10 * time.Second); err != nil {
-               t.Fatalf("Error waiting for leader: %s", err)
-       }
-       er := executeRequestFromString(`CREATE TABLE foo (id INTEGER NOT NULL 
PRIMARY KEY, name TEXT)`,
-               false, false)
-       _, _, err := s.Execute(context.Background(), er)
-       if err != nil {
-               t.Fatalf("failed to execute on single node: %s", err.Error())
-       }
-
-       er = executeRequestFromString(`INSERT INTO foo(name) VALUES("fiona")`, 
false, false)
-       _, _, err = s.Execute(context.Background(), er)
-       if err != nil {
-               t.Fatalf("failed to execute on single node: %s", err.Error())
-       }
-
-       ctx, cancelFunc := context.WithCancel(context.Background())
-       go func() {
-               qr := queryRequestFromString("SELECT * FROM foo", false, false)
-               qr.GetRequest().Statements[0].ForceStall = true
-
-               blockingDB, err := db.Open(s.dbPath, false, true)
-               if err != nil {
-                       t.Errorf("failed to open blocking DB connection: %s", 
err.Error())
-               }
-               defer blockingDB.Close()
-
-               _, err = blockingDB.QueryWithContext(ctx, qr.GetRequest(), 
false)
-               if err != nil {
-                       t.Errorf("failed to execute stalled query on blocking 
DB connection: %s", err.Error())
-               }
-       }()
-       time.Sleep(1 * time.Second)
-
-       er = executeRequestFromString(`INSERT INTO foo(name) VALUES("bob")`, 
false, false)
-       _, _, err = s.Execute(context.Background(), er)
-       if err != nil {
-               t.Fatalf("failed to execute on single node: %s", err.Error())
-       }
-
-       if err := s.Snapshot(0); err == nil {
-               t.Fatalf("expected error snapshotting single-node store with 
stalled query")
-       }
-
-       // Shutdown the blocking query so we can clean up. Windows in 
particular.
-       cancelFunc()
-       <-ctx.Done()
-}
-
 func Test_SingleNode_SnapshotWithAutoOptimize_Stress(t *testing.T) {
        s, ln := mustNewStore(t)
        defer ln.Close()

++++++ vendor.tar.xz ++++++

Reply via email to