Improve staleness check error handling and optimization (bd-n4td, bd-o4qy, bd-c4rq)
This commit implements three related improvements to database staleness checking: **bd-n4td (P2): Add warning when staleness check errors** - Added stderr warnings when CheckStaleness fails in ensureDatabaseFresh - Users now see "Warning: could not check database staleness: <error>" - Provides visibility into staleness check failures while allowing operations to proceed **bd-o4qy (P2): Improve CheckStaleness error handling** - Updated CheckStaleness to distinguish between expected and abnormal conditions: * Returns (false, nil) for expected "no data yet" scenarios (missing metadata, missing JSONL) * Returns (false, err) for abnormal errors (glob failures, permission errors) - Updated RPC server (2 locations) to log staleness errors but allow requests to proceed - Prevents blocking operations due to transient staleness check issues - Added comprehensive function documentation **bd-c4rq (P3): Refactor staleness check to avoid function call overhead** - Moved daemon check from inside ensureDatabaseFresh to all 8 call sites - Avoids unnecessary function call when running in daemon mode - Updated: list.go, info.go, status.go, show.go, stale.go, duplicates.go, ready.go, validate.go - Extracted staleness functions to new staleness.go for better organization **Code review fixes:** - Removed dead code in CheckStaleness (unreachable jsonlPath == "" check) - Removed unused ensureDatabaseFreshQuiet function **Files changed:** - New: cmd/bd/staleness.go (extracted staleness checking functions) - Modified: 8 command files (added daemon check before staleness calls) - Modified: internal/autoimport/autoimport.go (improved error handling) - Modified: internal/rpc/server_export_import_auto.go (handle errors gracefully)
This commit is contained in:
@@ -250,36 +250,49 @@ func parseJSONL(jsonlData []byte, _ Notifier) ([]*types.Issue, error) {
|
||||
|
||||
// CheckStaleness checks if JSONL is newer than last import
|
||||
// dbPath is the full path to the database file
|
||||
//
|
||||
// Returns:
|
||||
// - (true, nil) if JSONL is newer than last import (database is stale)
|
||||
// - (false, nil) if database is fresh or no JSONL exists yet
|
||||
// - (false, err) if an abnormal error occurred (file system issues, permissions, etc.)
|
||||
func CheckStaleness(ctx context.Context, store storage.Storage, dbPath string) (bool, error) {
|
||||
lastImportStr, err := store.GetMetadata(ctx, "last_import_time")
|
||||
if err != nil {
|
||||
// No metadata yet - expected for first run
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
||||
lastImportTime, err := time.Parse(time.RFC3339, lastImportStr)
|
||||
if err != nil {
|
||||
// Corrupted metadata - not critical, assume not stale
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
||||
// Find JSONL using database directory
|
||||
dbDir := filepath.Dir(dbPath)
|
||||
pattern := filepath.Join(dbDir, "*.jsonl")
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
// Glob failed - this is abnormal
|
||||
return false, fmt.Errorf("failed to find JSONL file: %w", err)
|
||||
}
|
||||
|
||||
var jsonlPath string
|
||||
if err == nil && len(matches) > 0 {
|
||||
if len(matches) > 0 {
|
||||
jsonlPath = matches[0]
|
||||
} else {
|
||||
jsonlPath = filepath.Join(dbDir, "issues.jsonl")
|
||||
}
|
||||
|
||||
if jsonlPath == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
||||
stat, err := os.Stat(jsonlPath)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
if os.IsNotExist(err) {
|
||||
// JSONL doesn't exist - expected for new repo
|
||||
return false, nil
|
||||
}
|
||||
// Other stat error (permissions, etc.) - abnormal
|
||||
return false, fmt.Errorf("failed to stat JSONL file %s: %w", jsonlPath, err)
|
||||
}
|
||||
|
||||
|
||||
return stat.ModTime().After(lastImportTime), nil
|
||||
}
|
||||
|
||||
@@ -184,8 +184,13 @@ func (s *Server) checkAndAutoImportIfStale(req *Request) error {
|
||||
// Fast path: Check if JSONL is stale using cheap mtime check
|
||||
// This avoids reading/hashing JSONL on every request
|
||||
isStale, err := autoimport.CheckStaleness(ctx, store, dbPath)
|
||||
if err != nil || !isStale {
|
||||
return err
|
||||
if err != nil {
|
||||
// Log error but allow request to proceed (don't block on staleness check failure)
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to check staleness: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if !isStale {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Single-flight guard: Only allow one import at a time
|
||||
@@ -220,8 +225,13 @@ func (s *Server) checkAndAutoImportIfStale(req *Request) error {
|
||||
|
||||
// Double-check staleness after acquiring lock (another goroutine may have imported)
|
||||
isStale, err = autoimport.CheckStaleness(ctx, store, dbPath)
|
||||
if err != nil || !isStale {
|
||||
return err
|
||||
if err != nil {
|
||||
// Log error but allow request to proceed (don't block on staleness check failure)
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to check staleness: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if !isStale {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create timeout context for import operation (bd-8931, bd-1048)
|
||||
|
||||
Reference in New Issue
Block a user