1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-26 18:21:01 +00:00

refactor(cli): use time.Duration for scheduler frequency

Polling frequency is undocumented so it's not exacly clear what units were.
This commit is contained in:
gudvinr 2025-08-18 23:10:18 +03:00 committed by Frédéric Guillot
parent 4af12a4129
commit 7060ecc163
4 changed files with 63 additions and 23 deletions

View file

@ -26,12 +26,12 @@ func runScheduler(store *storage.Storage, pool *worker.Pool) {
go cleanupScheduler( go cleanupScheduler(
store, store,
config.Opts.CleanupFrequencyHours(), config.Opts.CleanupFrequency(),
) )
} }
func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize, errorLimit, limitPerHost int) { func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency time.Duration, batchSize, errorLimit, limitPerHost int) {
for range time.Tick(time.Duration(frequency) * time.Minute) { for range time.Tick(frequency) {
// Generate a batch of feeds for any user that has feeds to refresh. // Generate a batch of feeds for any user that has feeds to refresh.
batchBuilder := store.NewBatchBuilder() batchBuilder := store.NewBatchBuilder()
batchBuilder.WithBatchSize(batchSize) batchBuilder.WithBatchSize(batchSize)
@ -49,8 +49,8 @@ func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSi
} }
} }
func cleanupScheduler(store *storage.Storage, frequency int) { func cleanupScheduler(store *storage.Storage, frequency time.Duration) {
for range time.Tick(time.Duration(frequency) * time.Hour) { for range time.Tick(frequency) {
runCleanupTasks(store) runCleanupTasks(store)
} }
} }

View file

@ -589,12 +589,22 @@ func TestDefaultCleanupFrequencyHoursValue(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err) t.Fatalf(`Parsing failure: %v`, err)
} }
expected := defaultCleanupFrequencyHours expected := defaultCleanupFrequency
result := opts.CleanupFrequencyHours() result := opts.CleanupFrequency()
if result != expected { if result != expected {
t.Fatalf(`Unexpected CLEANUP_FREQUENCY_HOURS value, got %v instead of %v`, result, expected) t.Fatalf(`Unexpected CLEANUP_FREQUENCY_HOURS value, got %v instead of %v`, result, expected)
} }
sorted := opts.SortedOptions(false)
i := slices.IndexFunc(sorted, func(opt *option) bool {
return opt.Key == "CLEANUP_FREQUENCY_HOURS"
})
expectedSerialized := int(defaultCleanupFrequency / time.Hour)
if got := sorted[i].Value; got != expectedSerialized {
t.Fatalf(`Unexpected value in option output, got %q instead of %q`, got, expectedSerialized)
}
} }
func TestCleanupFrequencyHours(t *testing.T) { func TestCleanupFrequencyHours(t *testing.T) {
@ -608,12 +618,22 @@ func TestCleanupFrequencyHours(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err) t.Fatalf(`Parsing failure: %v`, err)
} }
expected := 42 expected := 42 * time.Hour
result := opts.CleanupFrequencyHours() result := opts.CleanupFrequency()
if result != expected { if result != expected {
t.Fatalf(`Unexpected CLEANUP_FREQUENCY_HOURS value, got %v instead of %v`, result, expected) t.Fatalf(`Unexpected CLEANUP_FREQUENCY_HOURS value, got %v instead of %v`, result, expected)
} }
sorted := opts.SortedOptions(false)
i := slices.IndexFunc(sorted, func(opt *option) bool {
return opt.Key == "CLEANUP_FREQUENCY_HOURS"
})
expectedSerialized := 42
if got := sorted[i].Value; got != expectedSerialized {
t.Fatalf(`Unexpected value in option output, got %q instead of %q`, got, expectedSerialized)
}
} }
func TestDefaultCleanupArchiveReadDaysValue(t *testing.T) { func TestDefaultCleanupArchiveReadDaysValue(t *testing.T) {
@ -737,6 +757,16 @@ func TestDefaultPollingFrequencyValue(t *testing.T) {
if result != expected { if result != expected {
t.Fatalf(`Unexpected POLLING_FREQUENCY value, got %v instead of %v`, result, expected) t.Fatalf(`Unexpected POLLING_FREQUENCY value, got %v instead of %v`, result, expected)
} }
sorted := opts.SortedOptions(false)
i := slices.IndexFunc(sorted, func(opt *option) bool {
return opt.Key == "POLLING_FREQUENCY"
})
expectedSerialized := int(defaultPollingFrequency / time.Minute)
if got := sorted[i].Value; got != expectedSerialized {
t.Fatalf(`Unexpected value in option output, got %q instead of %q`, got, expectedSerialized)
}
} }
func TestPollingFrequency(t *testing.T) { func TestPollingFrequency(t *testing.T) {
@ -749,12 +779,22 @@ func TestPollingFrequency(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err) t.Fatalf(`Parsing failure: %v`, err)
} }
expected := 42 expected := 42 * time.Minute
result := opts.PollingFrequency() result := opts.PollingFrequency()
if result != expected { if result != expected {
t.Fatalf(`Unexpected POLLING_FREQUENCY value, got %v instead of %v`, result, expected) t.Fatalf(`Unexpected POLLING_FREQUENCY value, got %v instead of %v`, result, expected)
} }
sorted := opts.SortedOptions(false)
i := slices.IndexFunc(sorted, func(opt *option) bool {
return opt.Key == "POLLING_FREQUENCY"
})
expectedSerialized := 42
if got := sorted[i].Value; got != expectedSerialized {
t.Fatalf(`Unexpected value in option output, got %q instead of %q`, got, expectedSerialized)
}
} }
func TestDefaultForceRefreshInterval(t *testing.T) { func TestDefaultForceRefreshInterval(t *testing.T) {

View file

@ -28,7 +28,7 @@ const (
defaultRootURL = "http://localhost" defaultRootURL = "http://localhost"
defaultBasePath = "" defaultBasePath = ""
defaultWorkerPoolSize = 16 defaultWorkerPoolSize = 16
defaultPollingFrequency = 60 defaultPollingFrequency = 60 * time.Minute
defaultForceRefreshInterval = 30 * time.Second defaultForceRefreshInterval = 30 * time.Second
defaultBatchSize = 100 defaultBatchSize = 100
defaultPollingScheduler = "round_robin" defaultPollingScheduler = "round_robin"
@ -47,7 +47,7 @@ const (
defaultCertFile = "" defaultCertFile = ""
defaultKeyFile = "" defaultKeyFile = ""
defaultCertDomain = "" defaultCertDomain = ""
defaultCleanupFrequencyHours = 24 defaultCleanupFrequency = 24 * time.Hour
defaultCleanupArchiveReadDays = 60 defaultCleanupArchiveReadDays = 60
defaultCleanupArchiveUnreadDays = 180 defaultCleanupArchiveUnreadDays = 180
defaultCleanupArchiveBatchSize = 10000 defaultCleanupArchiveBatchSize = 10000
@ -125,7 +125,7 @@ type options struct {
certFile string certFile string
certDomain string certDomain string
certKeyFile string certKeyFile string
cleanupFrequencyHours int cleanupFrequencyInterval time.Duration
cleanupArchiveReadDays int cleanupArchiveReadDays int
cleanupArchiveUnreadDays int cleanupArchiveUnreadDays int
cleanupArchiveBatchSize int cleanupArchiveBatchSize int
@ -137,7 +137,7 @@ type options struct {
schedulerEntryFrequencyFactor int schedulerEntryFrequencyFactor int
schedulerRoundRobinMinInterval time.Duration schedulerRoundRobinMinInterval time.Duration
schedulerRoundRobinMaxInterval time.Duration schedulerRoundRobinMaxInterval time.Duration
pollingFrequency int pollingFrequency time.Duration
pollingLimitPerHost int pollingLimitPerHost int
pollingParsingErrorLimit int pollingParsingErrorLimit int
pollingScheduler string pollingScheduler string
@ -209,7 +209,7 @@ func NewOptions() *options {
certFile: defaultCertFile, certFile: defaultCertFile,
certDomain: defaultCertDomain, certDomain: defaultCertDomain,
certKeyFile: defaultKeyFile, certKeyFile: defaultKeyFile,
cleanupFrequencyHours: defaultCleanupFrequencyHours, cleanupFrequencyInterval: defaultCleanupFrequency,
cleanupArchiveReadDays: defaultCleanupArchiveReadDays, cleanupArchiveReadDays: defaultCleanupArchiveReadDays,
cleanupArchiveUnreadDays: defaultCleanupArchiveUnreadDays, cleanupArchiveUnreadDays: defaultCleanupArchiveUnreadDays,
cleanupArchiveBatchSize: defaultCleanupArchiveBatchSize, cleanupArchiveBatchSize: defaultCleanupArchiveBatchSize,
@ -361,9 +361,9 @@ func (o *options) CertDomain() string {
return o.certDomain return o.certDomain
} }
// CleanupFrequencyHours returns the interval in hours for cleanup jobs. // CleanupFrequencyHours returns the interval for cleanup jobs.
func (o *options) CleanupFrequencyHours() int { func (o *options) CleanupFrequency() time.Duration {
return o.cleanupFrequencyHours return o.cleanupFrequencyInterval
} }
// CleanupArchiveReadDays returns the number of days after which marking read items as removed. // CleanupArchiveReadDays returns the number of days after which marking read items as removed.
@ -402,7 +402,7 @@ func (o *options) BatchSize() int {
} }
// PollingFrequency returns the interval to refresh feeds in the background. // PollingFrequency returns the interval to refresh feeds in the background.
func (o *options) PollingFrequency() int { func (o *options) PollingFrequency() time.Duration {
return o.pollingFrequency return o.pollingFrequency
} }
@ -721,10 +721,10 @@ func (o *options) SortedOptions(redactSecret bool) []*option {
"BATCH_SIZE": o.batchSize, "BATCH_SIZE": o.batchSize,
"CERT_DOMAIN": o.certDomain, "CERT_DOMAIN": o.certDomain,
"CERT_FILE": o.certFile, "CERT_FILE": o.certFile,
"CLEANUP_FREQUENCY_HOURS": int(o.cleanupFrequencyInterval.Hours()),
"CLEANUP_ARCHIVE_BATCH_SIZE": o.cleanupArchiveBatchSize, "CLEANUP_ARCHIVE_BATCH_SIZE": o.cleanupArchiveBatchSize,
"CLEANUP_ARCHIVE_READ_DAYS": o.cleanupArchiveReadDays, "CLEANUP_ARCHIVE_READ_DAYS": o.cleanupArchiveReadDays,
"CLEANUP_ARCHIVE_UNREAD_DAYS": o.cleanupArchiveUnreadDays, "CLEANUP_ARCHIVE_UNREAD_DAYS": o.cleanupArchiveUnreadDays,
"CLEANUP_FREQUENCY_HOURS": o.cleanupFrequencyHours,
"CLEANUP_REMOVE_SESSIONS_DAYS": o.cleanupRemoveSessionsDays, "CLEANUP_REMOVE_SESSIONS_DAYS": o.cleanupRemoveSessionsDays,
"CREATE_ADMIN": o.createAdmin, "CREATE_ADMIN": o.createAdmin,
"DATABASE_CONNECTION_LIFETIME": o.databaseConnectionLifetime, "DATABASE_CONNECTION_LIFETIME": o.databaseConnectionLifetime,
@ -770,7 +770,7 @@ func (o *options) SortedOptions(redactSecret bool) []*option {
"OAUTH2_USER_CREATION": o.oauth2UserCreationAllowed, "OAUTH2_USER_CREATION": o.oauth2UserCreationAllowed,
"DISABLE_LOCAL_AUTH": o.disableLocalAuth, "DISABLE_LOCAL_AUTH": o.disableLocalAuth,
"FORCE_REFRESH_INTERVAL": int(o.forceRefreshInterval.Seconds()), "FORCE_REFRESH_INTERVAL": int(o.forceRefreshInterval.Seconds()),
"POLLING_FREQUENCY": o.pollingFrequency, "POLLING_FREQUENCY": int(o.pollingFrequency.Minutes()),
"POLLING_LIMIT_PER_HOST": o.pollingLimitPerHost, "POLLING_LIMIT_PER_HOST": o.pollingLimitPerHost,
"POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit, "POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit,
"POLLING_SCHEDULER": o.pollingScheduler, "POLLING_SCHEDULER": o.pollingScheduler,

View file

@ -127,7 +127,7 @@ func (p *parser) parseLines(lines []string) (err error) {
case "CERT_DOMAIN": case "CERT_DOMAIN":
p.opts.certDomain = parseString(value, defaultCertDomain) p.opts.certDomain = parseString(value, defaultCertDomain)
case "CLEANUP_FREQUENCY_HOURS": case "CLEANUP_FREQUENCY_HOURS":
p.opts.cleanupFrequencyHours = parseInt(value, defaultCleanupFrequencyHours) p.opts.cleanupFrequencyInterval = parseInterval(value, time.Hour, defaultCleanupFrequency)
case "CLEANUP_ARCHIVE_READ_DAYS": case "CLEANUP_ARCHIVE_READ_DAYS":
p.opts.cleanupArchiveReadDays = parseInt(value, defaultCleanupArchiveReadDays) p.opts.cleanupArchiveReadDays = parseInt(value, defaultCleanupArchiveReadDays)
case "CLEANUP_ARCHIVE_UNREAD_DAYS": case "CLEANUP_ARCHIVE_UNREAD_DAYS":
@ -143,7 +143,7 @@ func (p *parser) parseLines(lines []string) (err error) {
case "BATCH_SIZE": case "BATCH_SIZE":
p.opts.batchSize = parseInt(value, defaultBatchSize) p.opts.batchSize = parseInt(value, defaultBatchSize)
case "POLLING_FREQUENCY": case "POLLING_FREQUENCY":
p.opts.pollingFrequency = parseInt(value, defaultPollingFrequency) p.opts.pollingFrequency = parseInterval(value, time.Minute, defaultPollingFrequency)
case "POLLING_LIMIT_PER_HOST": case "POLLING_LIMIT_PER_HOST":
p.opts.pollingLimitPerHost = parseInt(value, 0) p.opts.pollingLimitPerHost = parseInt(value, 0)
case "POLLING_PARSING_ERROR_LIMIT": case "POLLING_PARSING_ERROR_LIMIT":