search: more server-search refinement

fixed bugs, added extra text on "no results found" to suggest server
searching, and conditionally disable the button based on search content
and current sort. Activities page still broken. Also fixed up cache
generation, only one should ever run now, as should sorting. Two time
thresholds exist, one to trigger a re-sync but do it in the background
(i.e. send the old one to the requester), and one to re-sync and wait
for it.
This commit is contained in:
Harvey Tindall
2025-05-21 15:20:39 +01:00
parent 94e076401e
commit 732ce1bc57
10 changed files with 281 additions and 149 deletions
+75 -24
View File
@@ -11,8 +11,12 @@ import (
const (
// FIXME: Follow mediabrowser, or make tuneable, or both
WEB_USER_CACHE_SYNC = 30 * time.Second
USER_DEFAULT_SORT_FIELD = "name"
// After cache is this old, re-sync, but do it in the background and return the old cache.
WEB_USER_CACHE_SYNC = 30 * time.Second
// After cache is this old, re-sync and wait for it and return the new cache.
WEB_USER_CACHE_WAIT_FOR_SYNC = 5 * time.Minute
USER_DEFAULT_SORT_FIELD = "name"
USER_DEFAULT_SORT_ASCENDING = true
)
type UserCache struct {
@@ -20,30 +24,67 @@ type UserCache struct {
Ref []*respUser
Sorted bool
LastSync time.Time
Lock sync.Mutex
SyncLock sync.Mutex
Syncing bool
SortLock sync.Mutex
Sorting bool
}
// FIXME: If shouldSync, sync in background and return old version. If shouldWaitForSync, wait for sync and return new one.
// FIXME: If locked, just wait for unlock and return someone elses work.
func (c *UserCache) gen(app *appContext) error {
// FIXME: I don't like this.
if !time.Now().After(c.LastSync.Add(WEB_USER_CACHE_SYNC)) {
shouldWaitForSync := time.Now().After(c.LastSync.Add(WEB_USER_CACHE_WAIT_FOR_SYNC)) || c.Ref == nil || len(c.Ref) == 0
shouldSync := time.Now().After(c.LastSync.Add(WEB_USER_CACHE_SYNC))
if !shouldSync {
return nil
}
c.Lock.Lock()
users, err := app.jf.GetUsers(false)
if err != nil {
syncStatus := make(chan error)
go func(status chan error, c *UserCache) {
c.SyncLock.Lock()
alreadySyncing := c.Syncing
// We're either already syncing or will be
c.Syncing = true
c.SyncLock.Unlock()
if !alreadySyncing {
users, err := app.jf.GetUsers(false)
if err != nil {
c.SyncLock.Lock()
c.Syncing = false
c.SyncLock.Unlock()
status <- err
return
}
cache := make([]respUser, len(users))
for i, jfUser := range users {
cache[i] = app.userSummary(jfUser)
}
ref := make([]*respUser, len(cache))
for i := range cache {
ref[i] = &(cache[i])
}
c.Cache = cache
c.Ref = ref
c.Sorted = false
c.LastSync = time.Now()
c.SyncLock.Lock()
c.Syncing = false
c.SyncLock.Unlock()
} else {
for c.Syncing {
continue
}
}
status <- nil
}(syncStatus, c)
if shouldWaitForSync {
err := <-syncStatus
return err
}
c.Cache = make([]respUser, len(users))
for i, jfUser := range users {
c.Cache[i] = app.userSummary(jfUser)
}
c.Ref = make([]*respUser, len(c.Cache))
for i := range c.Cache {
c.Ref[i] = &(c.Cache[i])
}
c.Sorted = false
c.LastSync = time.Now()
c.Lock.Unlock()
return nil
}
@@ -52,11 +93,21 @@ func (c *UserCache) Gen(app *appContext, sorted bool) ([]*respUser, error) {
return nil, err
}
if sorted && !c.Sorted {
c.Lock.Lock()
// FIXME: Check we want ascending!
c.Sort(c.Ref, USER_DEFAULT_SORT_FIELD, true)
c.Sorted = true
c.Lock.Unlock()
c.SortLock.Lock()
alreadySorting := c.Sorting
c.Sorting = true
c.SortLock.Unlock()
if !alreadySorting {
c.Sort(c.Ref, USER_DEFAULT_SORT_FIELD, USER_DEFAULT_SORT_ASCENDING)
c.Sorted = true
c.SortLock.Lock()
c.Sorting = false
c.SortLock.Unlock()
} else {
for c.Sorting {
continue
}
}
}
return c.Ref, nil
}