feat(search): gate index events by active provider and harden rebuild reconcile

Completes the search-provider-interface wiring that the catalog hardening
commits already call into:

- Skip the transactional search-index-event write path when Meilisearch is
  not the active provider (ItemRepository.WithActiveSearchProvider /
  SearchIndexEventRepository.disabledByActiveProvider).
- Dead-letter catalog_search_index_events after 10 attempts instead of
  retrying forever.
- Track the rebuild high-water mark (MaxEventID / MarkProcessedThrough) and
  persist last_processed_event_id in UpdateStateAfterRebuild so a rebuild
  reconciles events enqueued during the rebuild.
- Validate (read-only) the embedding lock when embedding a search query
  instead of establishing/mutating it.
- Surface total_exact on the legacy /items browse response.
- Wire the active catalog search provider into the scanner and item repo at
  startup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Quick
2026-06-26 08:34:04 -04:00
co-authored by Claude Opus 4.8
parent 91b5105f3c
commit ed6c084c68
10 changed files with 180 additions and 15 deletions
+8 -6
View File
@@ -265,9 +265,10 @@ type itemListImageURLs struct {
// browseResponse is the paginated response for the /items endpoint.
type browseResponse struct {
Total int `json:"total"`
HasMore bool `json:"has_more"`
Items []itemListResponse `json:"items"`
Total int `json:"total"`
TotalExact bool `json:"total_exact"`
HasMore bool `json:"has_more"`
Items []itemListResponse `json:"items"`
}
type itemFiltersResponse struct {
@@ -600,9 +601,10 @@ func (h *ItemsHandler) writeCatalogBrowseResponse(w http.ResponseWriter, r *http
}
writeJSON(w, http.StatusOK, browseResponse{
Total: result.Total,
HasMore: result.HasMore,
Items: items,
Total: result.Total,
TotalExact: result.TotalExact,
HasMore: result.HasMore,
Items: items,
})
return true
}
@@ -2,12 +2,29 @@ package handlers
import (
"context"
"encoding/json"
"strings"
"testing"
"github.com/Silo-Server/silo-server/internal/catalog"
"github.com/Silo-Server/silo-server/internal/models"
)
func TestBrowseResponseIncludesTotalExact(t *testing.T) {
data, err := json.Marshal(browseResponse{
Total: 3,
TotalExact: true,
HasMore: false,
Items: []itemListResponse{},
})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(data), `"total_exact":true`) {
t.Fatalf("browse response missing total_exact: %s", data)
}
}
type countingItemListImageResolver struct {
singleCalls int
batchCalls int