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
})
// 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.
startSafe(func() error {
defer close(records)

View File

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