Report a zero-length or missing database that starts empty (closes #290)
check / check (push) Failing after 1m11s
check / check (push) Failing after 1m11s
Audit of the configuration and state read from files. Three cases carried on silently: a zero-length webhooker.db booted as a first start without the "created a new, empty database" warning, and a missing or zero-length per-webhook database was replaced by an empty one with only an INFO line, losing that webhook's events and pending deliveries unannounced. All three now log that warning with the file's path; CreateDB, which makes a new webhook's file, does not. The README now says how .env, the main, per-webhook and archive databases treat an empty file, and that a damaged per-webhook database fails that webhook alone. Model: opus-5-5
This commit is contained in:
@@ -85,10 +85,24 @@ func NewWebhookDBManager(
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetDB returns the database connection for a webhook,
|
||||
// creating the database file lazily if it doesn't exist.
|
||||
// GetDB returns the database connection for a webhook, opening it on
|
||||
// first use.
|
||||
//
|
||||
// The file is made by CreateDB when the webhook is created. One that is
|
||||
// missing or zero-length here means the webhook's events and pending
|
||||
// deliveries are gone: an empty database is created in its place so
|
||||
// the webhook keeps receiving, and that is logged as a warning naming
|
||||
// the file, as a new main database is.
|
||||
func (m *WebhookDBManager) GetDB(
|
||||
webhookID string,
|
||||
) (*gorm.DB, error) {
|
||||
return m.getDB(webhookID, false)
|
||||
}
|
||||
|
||||
// getDB is GetDB, and CreateDB when isNew is true: the webhook has just
|
||||
// been created, so a missing file is expected rather than lost.
|
||||
func (m *WebhookDBManager) getDB(
|
||||
webhookID string, isNew bool,
|
||||
) (*gorm.DB, error) {
|
||||
// Fast path: already open
|
||||
if val, ok := m.dbs.Load(webhookID); ok {
|
||||
@@ -105,7 +119,7 @@ func (m *WebhookDBManager) GetDB(
|
||||
return asGormDB(val, webhookID)
|
||||
}
|
||||
|
||||
db, err := m.openDB(webhookID)
|
||||
db, err := m.openDB(webhookID, isNew)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -130,12 +144,12 @@ func asGormDB(val any, webhookID string) (*gorm.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// CreateDB explicitly creates a new per-webhook database file
|
||||
// and runs migrations.
|
||||
// CreateDB creates a new webhook's database file and runs
|
||||
// migrations.
|
||||
func (m *WebhookDBManager) CreateDB(
|
||||
webhookID string,
|
||||
) error {
|
||||
_, err := m.GetDB(webhookID)
|
||||
_, err := m.getDB(webhookID, true)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -242,12 +256,15 @@ func (m *WebhookDBManager) dbPath(
|
||||
}
|
||||
|
||||
// openDB opens (or creates) a per-webhook SQLite database and
|
||||
// runs migrations.
|
||||
// runs migrations. isNew is as for getDB.
|
||||
func (m *WebhookDBManager) openDB(
|
||||
webhookID string,
|
||||
webhookID string, isNew bool,
|
||||
) (*gorm.DB, error) {
|
||||
path := m.dbPath(webhookID)
|
||||
|
||||
// Checked before opening, which creates the file. See GetDB.
|
||||
replaced := !isNew && missingOrEmpty(path)
|
||||
|
||||
// See sqlite_open.go: WAL, a busy timeout, immediate-transaction
|
||||
// locking, and a bounded pool, all of which this file needs most —
|
||||
// it is the one every delivery worker writes to concurrently.
|
||||
@@ -319,6 +336,14 @@ func (m *WebhookDBManager) openDB(
|
||||
)
|
||||
}
|
||||
|
||||
if replaced {
|
||||
m.log.Warn(
|
||||
"created a new, empty database",
|
||||
"webhook_id", webhookID,
|
||||
"path", path,
|
||||
)
|
||||
}
|
||||
|
||||
m.log.Info(
|
||||
"opened per-webhook database",
|
||||
"webhook_id", webhookID,
|
||||
|
||||
Reference in New Issue
Block a user