Report a zero-length or missing database that starts empty (closes #290)
check / check (push) Successful in 3m26s
check / check (push) Successful in 3m26s
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:
@@ -98,34 +98,18 @@ 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) {
|
||||
// Fast path: already open
|
||||
if val, ok := m.dbs.Load(webhookID); ok {
|
||||
return asGormDB(val, webhookID)
|
||||
}
|
||||
|
||||
// Slow path: open the database under the lock, looking in the
|
||||
// cache again first. A caller that raced another one here then
|
||||
// waits for its handle instead of opening a second one.
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if val, ok := m.dbs.Load(webhookID); ok {
|
||||
return asGormDB(val, webhookID)
|
||||
}
|
||||
|
||||
db, err := m.openDB(webhookID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.dbs.Store(webhookID, db)
|
||||
|
||||
return db, nil
|
||||
return m.getDB(webhookID, false)
|
||||
}
|
||||
|
||||
// asGormDB returns a value read from the cache as the database
|
||||
@@ -143,12 +127,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
|
||||
}
|
||||
@@ -266,6 +250,48 @@ func (m *WebhookDBManager) DBPath(
|
||||
return m.dbPath(webhookID)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return asGormDB(val, webhookID)
|
||||
}
|
||||
|
||||
// Slow path: open the database under the lock, looking in the
|
||||
// cache again first. A caller that raced another one here then
|
||||
// waits for its handle instead of opening a second one.
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if val, ok := m.dbs.Load(webhookID); ok {
|
||||
return asGormDB(val, webhookID)
|
||||
}
|
||||
|
||||
// Checked before opening, which creates the file. See GetDB.
|
||||
path := m.dbPath(webhookID)
|
||||
replaced := !isNew && missingOrEmpty(path)
|
||||
|
||||
db, err := m.openDB(webhookID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if replaced {
|
||||
m.log.Warn(
|
||||
"created a new, empty database",
|
||||
"webhook_id", webhookID,
|
||||
"path", path,
|
||||
)
|
||||
}
|
||||
|
||||
m.dbs.Store(webhookID, db)
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (m *WebhookDBManager) dbPath(
|
||||
webhookID string,
|
||||
) string {
|
||||
|
||||
Reference in New Issue
Block a user