Unnecessary filterlists database update on service start

https://github.com/safing/portmaster/issues/1988

---
The filterlists database is often updated when the service starts, even if the latest version is already cached in the database.
This does not affect PM’s functionality, but it unnecessarily consumes CPU resources.

**Technical explanation:**
This happens because the filterlists update check sometimes runs before the module is fully initialized.

**Steps to Reproduce:**
1. Start PM and ensure the filterlists are up to date.
2. Fully stop PM.
3. Start PM again.

**Observed result:**
PM reprocesses the filterlists cache database.

**Expected result:**
PM should update only when new data is available. Already processed data should not be reprocessed.
This commit is contained in:
Alexandr Stelnykovych
2025-08-28 17:21:17 +03:00
parent f7a8133f81
commit 1ee7db6948
2 changed files with 17 additions and 21 deletions

View File

@@ -118,14 +118,6 @@ func processListFile(ctx context.Context, filter *scopedBloom, file *updates.Art
return return
}) })
// Wait for the module initialization to complete to ensure the filter is fully loaded.
// This avoids prolonged locks during filter updates caused by concurrent database loading.
select {
case <-moduleInitDone:
case <-time.After(time.Second * 20):
log.Warning("intel/filterlists: timeout waiting for module initialization")
}
// Process each entry and send records to records channel. // Process each entry and send records to records channel.
startSafe(func() error { startSafe(func() error {
defer close(records) defer close(records)

View File

@@ -35,9 +35,6 @@ func (fl *FilterLists) States() *mgr.StateMgr {
} }
func (fl *FilterLists) Start() error { func (fl *FilterLists) Start() error {
if err := prep(); err != nil {
return err
}
return start() return start()
} }
@@ -46,26 +43,21 @@ func (fl *FilterLists) Stop() error {
} }
var ( var (
moduleInitDone chan struct{}
// booleans mainly used to decouple the module during testing. // booleans mainly used to decouple the module during testing.
ignoreUpdateEvents = abool.New() ignoreUpdateEvents = abool.New()
ignoreNetEnvEvents = abool.New() ignoreNetEnvEvents = abool.New()
) )
func init() { func init() {
moduleInitDone = make(chan struct{})
ignoreNetEnvEvents.Set() ignoreNetEnvEvents.Set()
} }
func prep() error { func registerEventCallbacks() error {
module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("Check for blocklist updates", module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("Check for blocklist updates",
func(wc *mgr.WorkerCtx, s struct{}) (bool, error) { func(wc *mgr.WorkerCtx, s struct{}) (bool, error) {
if ignoreUpdateEvents.IsSet() { if ignoreUpdateEvents.IsSet() {
return false, nil return false, nil
} }
log.Debugf("performing filter list update")
return false, tryListUpdate(wc.Ctx()) return false, tryListUpdate(wc.Ctx())
}) })
@@ -90,9 +82,22 @@ func start() error {
filterListLock.Lock() filterListLock.Lock()
defer filterListLock.Unlock() defer filterListLock.Unlock()
// Signal that the module has been initialized. // Any call of tryListUpdate() must be only after module fully initialized
// This indicates that the module is ready for use, with the default filter defer func() {
defer close(moduleInitDone) // Register event callbacks
if err := registerEventCallbacks(); err != nil {
log.Errorf("intel/filterlists: failed to register callbacks: %q", err.Error())
}
// Initial check filterlists updates
module.Manager().Go("intel/filterlists inital check for update", func(ctx *mgr.WorkerCtx) error {
if err := tryListUpdate(ctx.Ctx()); err != nil {
log.Errorf("intel/filterlists: tryListUpdate() failed: %q", err.Error())
return err
}
return nil
})
}()
ver, err := getCacheDatabaseVersion() ver, err := getCacheDatabaseVersion()
if err == nil { if err == nil {
@@ -115,7 +120,6 @@ func start() error {
} }
func stop() error { func stop() error {
moduleInitDone = make(chan struct{})
filterListsLoaded = make(chan struct{}) filterListsLoaded = make(chan struct{})
return nil return nil
} }