Query: ensure that only one AsyncMatchMarkers can be executing at a time

This commit is contained in:
Keith Martin 2026-06-12 10:54:19 +10:00
parent b36b2616ea
commit 3ddb1aea46
2 changed files with 25 additions and 0 deletions

View file

@ -6,6 +6,7 @@ import (
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/jinzhu/gorm"
@ -29,6 +30,9 @@ var ErrRetainedManualClusters = errors.New("faces: retained manual clusters afte
// MergeMaxRetry limits how often the optimizer retries stubborn manual clusters (0 = unlimited).
var MergeMaxRetry = 1
// MatchMarkersMutex guards concurrent MatchMarkers updates running.
var MatchMarkersMutex = &sync.Mutex{}
func init() {
if v := os.Getenv("PHOTOPRISM_FACE_MERGE_MAX_RETRY"); v != "" {
if n, err := strconv.Atoi(v); err == nil {
@ -280,6 +284,8 @@ func ProcessMatchMarkersAsync(m *entity.Face, faceIDs []string) error {
}
go func() {
MatchMarkersMutex.Lock()
defer MatchMarkersMutex.Unlock()
log.Debugf("faces: async matching commenced for %s", m.ID)
if err := m.MatchMarkersAsync(faceIDs); err != nil {
log.Warnf("faces: %s (match markers)", clean.Error(err))

View file

@ -1,7 +1,9 @@
package query
import (
"bytes"
"errors"
"os"
"path/filepath"
"testing"
"time"
@ -358,4 +360,21 @@ func TestProcessMatchMarkersAsync(t *testing.T) {
require.Nil(t, ProcessMatchMarkersAsync(entity.FindFace(entity.FaceFixtures.Get("john-doe").ID), nil))
time.Sleep(time.Second)
})
t.Run("Mutex", func(t *testing.T) {
// Setup and capture Logging output
log.Debug("TestProcessMatchMarkersAsync/Mutex Commencing")
buffer := bytes.Buffer{}
log.SetOutput(&buffer)
MatchMarkersMutex.Lock()
require.Nil(t, ProcessMatchMarkersAsync(entity.FindFace(entity.FaceFixtures.Get("john-doe").ID), entity.Faceless))
log.Debug("TestProcessMatchMarkersAsync/Mutex Waiting 1s")
time.Sleep(time.Second)
assert.NotContains(t, buffer.String(), "faces: async matching commenced for")
MatchMarkersMutex.Unlock()
time.Sleep(2 * time.Second)
assert.Contains(t, buffer.String(), "faces: async matching commenced for")
// Reset logger
log.SetOutput(os.Stdout)
log.Debug(buffer.String())
})
}