aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend/cmd/serve.go
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-04-27 21:20:10 +0900
committernsfisis <nsfisis@gmail.com>2026-04-27 21:20:10 +0900
commitcb00405041ee4714b6e817e9570cfa10ae972840 (patch)
treeff98728b3e5e099eb9ac5556eeb407c68e0fc208 /backend/cmd/serve.go
parent938863425bf8ad6c17e43b3da128f92cf6d6ab63 (diff)
downloadfeedaka-cb00405041ee4714b6e817e9570cfa10ae972840.tar.gz
feedaka-cb00405041ee4714b6e817e9570cfa10ae972840.tar.zst
feedaka-cb00405041ee4714b6e817e9570cfa10ae972840.zip
feat(backend): adapt feed fetch interval to update frequencyHEADmain
Add per-feed fetch_interval_seconds (clamped to [1h, 24h]) that halves on new articles and grows 1.5x when a fetch yields nothing, replacing the fixed 1h schedule with the 10min cooldown filter. Scheduler tick shortened to 30min so the 1h floor is honored with reasonable precision.
Diffstat (limited to 'backend/cmd/serve.go')
-rw-r--r--backend/cmd/serve.go68
1 files changed, 42 insertions, 26 deletions
diff --git a/backend/cmd/serve.go b/backend/cmd/serve.go
index 4b32868..75bcb53 100644
--- a/backend/cmd/serve.go
+++ b/backend/cmd/serve.go
@@ -23,46 +23,62 @@ import (
"undef.ninja/x/feedaka/feed"
)
-func fetchOneFeed(feedID int64, url string, ctx context.Context, queries *db.Queries) error {
- log.Printf("Fetching %s...\n", url)
- result, err := feed.Fetch(ctx, url)
- if err != nil {
- return err
+const (
+ minFetchIntervalSeconds = 60 * 60 // 1 hour
+ maxFetchIntervalSeconds = 24 * 60 * 60 // 1 day
+)
+
+// nextFetchInterval adapts the per-feed poll interval based on whether the
+// last fetch yielded new articles. Active feeds converge toward the floor
+// quickly (halving) while quiet feeds back off gently (1.5x) so a brief lull
+// doesn't push the interval to the ceiling.
+func nextFetchInterval(current int64, hadNewArticles bool) int64 {
+ var next int64
+ if hadNewArticles {
+ next = current / 2
+ } else {
+ next = current * 3 / 2
}
- return feed.Sync(ctx, queries, feedID, result.Feed)
+ if next < minFetchIntervalSeconds {
+ next = minFetchIntervalSeconds
+ }
+ if next > maxFetchIntervalSeconds {
+ next = maxFetchIntervalSeconds
+ }
+ return next
}
-func listFeedsToBeFetched(ctx context.Context, queries *db.Queries) (map[int64]string, error) {
- feeds, err := queries.GetFeedsToFetch(ctx)
+func fetchOneFeed(ctx context.Context, queries *db.Queries, f db.GetFeedsToFetchRow) error {
+ log.Printf("Fetching %s...\n", f.Url)
+ result, err := feed.Fetch(ctx, f.Url)
+ if err != nil {
+ return err
+ }
+ newCount, err := feed.Sync(ctx, queries, f.ID, result.Feed)
if err != nil {
- return nil, err
+ return err
}
-
- result := make(map[int64]string)
- for _, feed := range feeds {
- fetchedAtTime, err := time.Parse(time.RFC3339, feed.FetchedAt)
- if err != nil {
- log.Fatal(err)
- }
- now := time.Now().UTC()
- if now.Sub(fetchedAtTime).Minutes() <= 10 {
- continue
+ next := nextFetchInterval(f.FetchIntervalSeconds, newCount > 0)
+ if next != f.FetchIntervalSeconds {
+ if err := queries.UpdateFeedFetchInterval(ctx, db.UpdateFeedFetchIntervalParams{
+ FetchIntervalSeconds: next,
+ ID: f.ID,
+ }); err != nil {
+ return err
}
- result[feed.ID] = feed.Url
}
- return result, nil
+ return nil
}
func fetchAllFeeds(ctx context.Context, queries *db.Queries) error {
- feeds, err := listFeedsToBeFetched(ctx, queries)
+ feeds, err := queries.GetFeedsToFetch(ctx)
if err != nil {
return err
}
var result *multierror.Error
- for feedID, url := range feeds {
- err := fetchOneFeed(feedID, url, ctx, queries)
- if err != nil {
+ for _, f := range feeds {
+ if err := fetchOneFeed(ctx, queries, f); err != nil {
result = multierror.Append(result, err)
}
time.Sleep(5 * time.Second)
@@ -121,7 +137,7 @@ func RunServe(database *sql.DB, cfg *config.Config, publicFS embed.FS) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- scheduled(ctx, 1*time.Hour, func() {
+ scheduled(ctx, 30*time.Minute, func() {
err := fetchAllFeeds(ctx, queries)
if err != nil {
log.Printf("Failed to fetch feeds: %v\n", err)