Make -collect 0 a resumable, single-pass full re-check

Mode 0 (full re-check) previously reset its cutoff to now on every
launch, so an interrupted run restarted from scratch, and a post that
kept returning a non-Success/non-NotFound status could loop forever.

- Add single-row CollectRunState table + accessors (EnsureCollectRunStateTableExists,
  GetCollectRunState, BeginCollectRun, CompleteCollectRun) mirroring the
  ApiKeyPoolMeta pattern, to persist a frozen run cutoff and completion flag.
- -collect 0 with no explicit date is now a managed run: resume against the
  stored cutoff if a run is in progress, else start a new run; mark complete
  when the pass finishes so the next launch starts fresh. Explicit-date and
  mode 1 behavior unchanged.
- CollectNotes makes a single attempt pass via an in-process attempted set;
  FAILURE/UNKNOWN are logged once, TooManyRequests/no-lease aborts without
  completing so a later launch resumes.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
jim
2026-06-03 15:48:26 -05:00
co-authored by Claude Opus 4.8
parent b576a9cdf3
commit 18f172fe96
2 changed files with 123 additions and 5 deletions
+63
View File
@@ -1342,6 +1342,69 @@ namespace URLNotesGrabberCORE
}
}
// ----- CollectRunState: tracks the frozen cutoff + completion flag for a managed "-collect 0" full re-check run -----
// Single-row table (Id = 1), mirroring the ApiKeyPoolMeta pattern. Lets an interrupted run resume against the
// same cutoff and lets a completed run stop instead of restarting on the next launch.
public static void EnsureCollectRunStateTableExists(string? DBPath = null)
{
DBPath ??= GetDefaultDbPath();
using SQLiteConnection connection = new SQLiteConnection("Data Source=" + DBPath);
connection.Open();
string sql = "CREATE TABLE IF NOT EXISTS CollectRunState (" +
"Id INTEGER PRIMARY KEY CHECK (Id = 1), " +
"RunCutoff INTEGER DEFAULT 0, " +
"RunComplete INTEGER DEFAULT 1, " +
"RunStarted TEXT, " +
"RunCompletedAt TEXT)";
using SQLiteCommand command = new SQLiteCommand(sql, connection);
command.ExecuteNonQuery();
}
// Returns (RunCutoff unix seconds, RunComplete) for the single run-state row, or null if no row exists yet.
public static (long cutoff, bool complete)? GetCollectRunState(string? DBPath = null)
{
DBPath ??= GetDefaultDbPath();
using SQLiteConnection connection = new SQLiteConnection("Data Source=" + DBPath);
connection.Open();
using SQLiteCommand command = new SQLiteCommand("SELECT RunCutoff, RunComplete FROM CollectRunState WHERE Id = 1", connection);
using SQLiteDataReader reader = command.ExecuteReader();
if (reader.Read())
{
long cutoff = Convert.ToInt64(reader.GetValue(0));
bool complete = Convert.ToInt64(reader.GetValue(1)) != 0;
return (cutoff, complete);
}
return null;
}
// Start (or restart) a managed run: freeze the cutoff and mark the run in progress.
public static void BeginCollectRun(long cutoffUnixSeconds, string? DBPath = null)
{
DBPath ??= GetDefaultDbPath();
using SQLiteConnection connection = new SQLiteConnection("Data Source=" + DBPath);
connection.Open();
string sql = "INSERT INTO CollectRunState (Id, RunCutoff, RunComplete, RunStarted, RunCompletedAt) " +
"VALUES (1, @cutoff, 0, @started, NULL) " +
"ON CONFLICT(Id) DO UPDATE SET RunCutoff = @cutoff, RunComplete = 0, RunStarted = @started, RunCompletedAt = NULL";
using SQLiteCommand command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@cutoff", cutoffUnixSeconds);
command.Parameters.AddWithValue("@started", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
command.ExecuteNonQuery();
}
// Mark the active managed run complete so the next launch starts fresh instead of resuming.
public static void CompleteCollectRun(string? DBPath = null)
{
DBPath ??= GetDefaultDbPath();
using SQLiteConnection connection = new SQLiteConnection("Data Source=" + DBPath);
connection.Open();
string sql = "UPDATE CollectRunState SET RunComplete = 1, RunCompletedAt = @completedAt WHERE Id = 1";
using SQLiteCommand command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@completedAt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
command.ExecuteNonQuery();
}
public static void UpdatePostSetDate(string blogName, long postID, string postDate, string? DBPath = null)
{
DBPath ??= GetDefaultDbPath();