Exclude !post from the daily cap
Manual !post overrides were counted toward daily_cap_total, so a few forced posts could starve the round-robin rotation for the rest of the day. Tag forced rows in post_log and skip them in CountAllPostsInWindow so the cap only meters the auto-rotation.
This commit is contained in:
@@ -83,6 +83,7 @@ func runMigrations(d *sql.DB) error {
|
||||
addColumnIfMissing(d, "stories", "paywalled", "INTEGER NOT NULL DEFAULT 0")
|
||||
addColumnIfMissing(d, "stories", "published_at", "INTEGER")
|
||||
addColumnIfMissing(d, "post_log", "url_canonical", "TEXT")
|
||||
addColumnIfMissing(d, "post_log", "forced", "INTEGER NOT NULL DEFAULT 0")
|
||||
addColumnIfMissing(d, "round_robin_state", "last_channel", "TEXT")
|
||||
|
||||
// FTS5 virtual tables don't support IF NOT EXISTS reliably.
|
||||
|
||||
@@ -122,10 +122,16 @@ func GetStoryByGUID(guid string) (*Story, error) {
|
||||
|
||||
// InsertPostLog records that a story was posted to a channel.
|
||||
// Uses OR IGNORE to prevent duplicate posts for the same story+channel.
|
||||
func InsertPostLog(guid, channel, eventID, urlCanonical string) {
|
||||
// forced=true marks the row as a manual !post override so it doesn't count
|
||||
// against the global daily cap.
|
||||
func InsertPostLog(guid, channel, eventID, urlCanonical string, forced bool) {
|
||||
f := 0
|
||||
if forced {
|
||||
f = 1
|
||||
}
|
||||
exec("insert post_log",
|
||||
`INSERT OR IGNORE INTO post_log (guid, channel, event_id, url_canonical, posted_at) VALUES (?, ?, ?, ?, ?)`,
|
||||
guid, channel, eventID, nullIfEmpty(urlCanonical), nowUnix())
|
||||
`INSERT OR IGNORE INTO post_log (guid, channel, event_id, url_canonical, posted_at, forced) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
guid, channel, eventID, nullIfEmpty(urlCanonical), nowUnix(), f)
|
||||
}
|
||||
|
||||
// GetLastPostTime returns the unix timestamp of the most recent post to a channel.
|
||||
@@ -139,10 +145,11 @@ func GetLastPostTime(channel string) int64 {
|
||||
}
|
||||
|
||||
// CountAllPostsInWindow counts posts across ALL channels within a time window.
|
||||
// Used for the global daily cap.
|
||||
// Used for the global daily cap. Excludes forced (!post) entries so manual
|
||||
// overrides don't eat into the auto-rotation budget.
|
||||
func CountAllPostsInWindow(windowStart int64) int {
|
||||
var count int
|
||||
if err := Get().QueryRow(`SELECT COUNT(*) FROM post_log WHERE posted_at >= ?`, windowStart).Scan(&count); err != nil {
|
||||
if err := Get().QueryRow(`SELECT COUNT(*) FROM post_log WHERE posted_at >= ? AND forced = 0`, windowStart).Scan(&count); err != nil {
|
||||
slog.Error("CountAllPostsInWindow query failed", "err", err)
|
||||
return 1<<31 - 1 // fail closed: huge number prevents posting
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ CREATE TABLE IF NOT EXISTS post_log (
|
||||
channel TEXT NOT NULL,
|
||||
event_id TEXT,
|
||||
url_canonical TEXT,
|
||||
posted_at INTEGER NOT NULL
|
||||
posted_at INTEGER NOT NULL,
|
||||
forced INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS round_robin_state (
|
||||
|
||||
@@ -98,8 +98,8 @@ func TestMarkClassified(t *testing.T) {
|
||||
func TestPostLogAndLookup(t *testing.T) {
|
||||
setupTestDB(t)
|
||||
|
||||
InsertPostLog("story-1", "tech", "$event1:example.org", "")
|
||||
InsertPostLog("story-2", "politics", "$event2:example.org", "")
|
||||
InsertPostLog("story-1", "tech", "$event1:example.org", "", false)
|
||||
InsertPostLog("story-2", "politics", "$event2:example.org", "", false)
|
||||
|
||||
guid, channel, found := LookupPostGUID("$event1:example.org")
|
||||
if !found {
|
||||
@@ -193,9 +193,9 @@ func TestMarshalUnmarshalPlatforms(t *testing.T) {
|
||||
func TestInsertPostLog_DuplicateIgnored(t *testing.T) {
|
||||
setupTestDB(t)
|
||||
|
||||
InsertPostLog("story-1", "tech", "$event1:example.org", "")
|
||||
InsertPostLog("story-1", "tech", "$event1:example.org", "", false)
|
||||
// Same guid+channel should be silently ignored
|
||||
InsertPostLog("story-1", "tech", "$event1-retry:example.org", "")
|
||||
InsertPostLog("story-1", "tech", "$event1-retry:example.org", "", false)
|
||||
|
||||
var count int
|
||||
Get().QueryRow(`SELECT COUNT(*) FROM post_log WHERE guid = ? AND channel = ?`, "story-1", "tech").Scan(&count)
|
||||
@@ -204,7 +204,7 @@ func TestInsertPostLog_DuplicateIgnored(t *testing.T) {
|
||||
}
|
||||
|
||||
// Different channel should be allowed
|
||||
InsertPostLog("story-1", "politics", "$event2:example.org", "")
|
||||
InsertPostLog("story-1", "politics", "$event2:example.org", "", false)
|
||||
Get().QueryRow(`SELECT COUNT(*) FROM post_log WHERE guid = ?`, "story-1").Scan(&count)
|
||||
if count != 2 {
|
||||
t.Errorf("expected 2 post_log entries across channels, got %d", count)
|
||||
|
||||
Reference in New Issue
Block a user