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:
Steve Yegge
2025-11-20 20:45:09 -05:00
parent 2f6bcccdb6
commit 9088988edd
11 changed files with 169 additions and 15 deletions

View File

@@ -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
}

View File

@@ -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)