Skip to content

Commit dbf4e60

Browse files
committed
handle SQLite locking more gracefully
1 parent a6601af commit dbf4e60

3 files changed

Lines changed: 50 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [UNRELEASED]
99

10+
### Added
11+
- Add clipboard copy functionality for public keys (`c` in the public keys view).
12+
13+
### Fixed
14+
- Fix migration format for `golang-migrate` compatibility.
15+
16+
---
17+
## [1.3.6] - 2025-09-28
18+
19+
### Added
20+
- Add clipboard copy functionality for public keys (also on deployment dialog to display authorized_keys file contents)
21+
22+
1023
### Fixed
24+
- Fix migration format for golang-migrate compatability
1125

1226
---
1327

cmd/keymaster/main.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import (
1414
"errors"
1515
"fmt"
1616
"log"
17+
"math/rand"
1718
"os"
1819
"strings"
1920
"sync"
21+
"time"
2022

2123
"github.com/spf13/cobra"
2224
"github.com/spf13/viper"
@@ -446,8 +448,22 @@ func runDeploymentForAccount(account model.Account) error {
446448
return fmt.Errorf(i18n.T("deploy.error_deployment_failed", err))
447449
}
448450

449-
if err := db.UpdateAccountSerial(account.ID, activeKey.Serial); err != nil {
450-
return fmt.Errorf(i18n.T("deploy.error_db_update_failed", err))
451+
// Retry updating the database serial on "database is locked" errors.
452+
// This is critical to prevent state inconsistency after a successful deployment.
453+
var dbUpdateErr error
454+
for i := 0; i < 5; i++ { // Retry up to 5 times
455+
dbUpdateErr = db.UpdateAccountSerial(account.ID, activeKey.Serial)
456+
if dbUpdateErr == nil {
457+
return nil // Success
458+
}
459+
if strings.Contains(dbUpdateErr.Error(), "database is locked") {
460+
time.Sleep(time.Duration(50+rand.Intn(100)) * time.Millisecond) // Wait 50-150ms
461+
continue
462+
}
463+
break // It's a different error, don't retry
464+
}
465+
if dbUpdateErr != nil {
466+
return fmt.Errorf(i18n.T("deploy.error_db_update_failed", dbUpdateErr))
451467
}
452468

453469
return nil

internal/tui/deploy.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
// Keymaster - SSH key management system
33
// This source code is licensed under the MIT license found in the LICENSE file.
44

5-
// package tui provides the terminal user interface for Keymaster.
6-
// This file contains the logic for the deployment view, which allows users
7-
// to deploy keys to single accounts, tags, or the entire fleet.
85
package tui // import "github.com/toeirei/keymaster/internal/tui"
96

107
import (
118
"fmt"
9+
"math/rand"
1210
"sort"
1311
"strings"
12+
"time"
1413

1514
"github.com/atotto/clipboard"
1615
tea "github.com/charmbracelet/bubbletea"
@@ -623,8 +622,22 @@ func performDeploymentCmd(account model.Account) tea.Cmd {
623622
}
624623

625624
// 4. Update the database on success.
626-
if err := db.UpdateAccountSerial(account.ID, activeKey.Serial); err != nil {
627-
return deploymentResultMsg{account: account, err: fmt.Errorf(i18n.T("deploy.error_db_update_failed_tui", err))}
625+
// Retry updating the database serial on "database is locked" errors.
626+
// This is critical to prevent state inconsistency after a successful deployment.
627+
var dbUpdateErr error
628+
for i := 0; i < 5; i++ { // Retry up to 5 times
629+
dbUpdateErr = db.UpdateAccountSerial(account.ID, activeKey.Serial)
630+
if dbUpdateErr == nil {
631+
break // Success
632+
}
633+
if strings.Contains(dbUpdateErr.Error(), "database is locked") {
634+
time.Sleep(time.Duration(50+rand.Intn(100)) * time.Millisecond) // Wait 50-150ms
635+
continue
636+
}
637+
break // It's a different error, don't retry
638+
}
639+
if dbUpdateErr != nil {
640+
return deploymentResultMsg{account: account, err: fmt.Errorf(i18n.T("deploy.error_db_update_failed_tui", dbUpdateErr))}
628641
}
629642

630643
return deploymentResultMsg{account: account, err: nil} // Success

0 commit comments

Comments
 (0)