Fix browse filters having no effect due to filter-agnostic cache key
Some checks failed
Deploy / Deploy Production (push) Has been skipped
Deploy / Cleanup Preview (push) Has been skipped
Deploy / Deploy Preview (push) Failing after 1s
CI / Scraper / Lint (pull_request) Successful in 17s
CI / Scraper / Test (pull_request) Successful in 17s
CI / UI / Build (pull_request) Successful in 17s
CI / Scraper / Build (pull_request) Successful in 15s
Some checks failed
Deploy / Deploy Production (push) Has been skipped
Deploy / Cleanup Preview (push) Has been skipped
Deploy / Deploy Preview (push) Failing after 1s
CI / Scraper / Lint (pull_request) Successful in 17s
CI / Scraper / Test (pull_request) Successful in 17s
CI / UI / Build (pull_request) Successful in 17s
CI / Scraper / Build (pull_request) Successful in 15s
The MinIO cache key only encoded page number, so all filter combinations hit the same cache entry and returned unfiltered results. Introduce BrowseFilteredHTMLKey() that encodes sort/genre/status into the key (e.g. novelfire.net/html/new-isekai-completed/page-1.html); falls back to the original page-only key for default filters to preserve existing cached pages.
This commit is contained in:
@@ -155,8 +155,9 @@ func (s *mockStore) SaveBrowsePage(_ context.Context, _, _ string) error { retur
|
|||||||
func (s *mockStore) GetBrowsePage(_ context.Context, _ string) (string, bool, error) {
|
func (s *mockStore) GetBrowsePage(_ context.Context, _ string) (string, bool, error) {
|
||||||
return "", false, nil
|
return "", false, nil
|
||||||
}
|
}
|
||||||
func (s *mockStore) BrowseHTMLKey(_ string, _ int) string { return "" }
|
func (s *mockStore) BrowseHTMLKey(_ string, _ int) string { return "" }
|
||||||
func (s *mockStore) BrowseCoverKey(_, _ string) string { return "" }
|
func (s *mockStore) BrowseFilteredHTMLKey(_ string, _ int, _, _, _ string) string { return "" }
|
||||||
|
func (s *mockStore) BrowseCoverKey(_, _ string) string { return "" }
|
||||||
func (s *mockStore) SaveBrowseAsset(_ context.Context, _ string, _ []byte, _ string) error {
|
func (s *mockStore) SaveBrowseAsset(_ context.Context, _ string, _ []byte, _ string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func (s *Server) handleBrowse(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// ── Cache-first: try MinIO snapshot (new key layout) ─────────────────
|
// ── Cache-first: try MinIO snapshot (new key layout) ─────────────────
|
||||||
cacheKey := s.store.BrowseHTMLKey(novelFireDomain, pageNum)
|
cacheKey := s.store.BrowseFilteredHTMLKey(novelFireDomain, pageNum, sortBy, genre, status)
|
||||||
if html, ok, err := s.store.GetBrowsePage(ctx, cacheKey); err == nil && ok && len(html) > 0 {
|
if html, ok, err := s.store.GetBrowsePage(ctx, cacheKey); err == nil && ok && len(html) > 0 {
|
||||||
novels, hasNext := parseBrowsePage(strings.NewReader(html))
|
novels, hasNext := parseBrowsePage(strings.NewReader(html))
|
||||||
s.log.Debug("browse: served from cache", "key", cacheKey)
|
s.log.Debug("browse: served from cache", "key", cacheKey)
|
||||||
|
|||||||
@@ -330,6 +330,10 @@ func (h *HybridStore) BrowseHTMLKey(domain string, page int) string {
|
|||||||
return BrowseHTMLKey(domain, page)
|
return BrowseHTMLKey(domain, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HybridStore) BrowseFilteredHTMLKey(domain string, page int, sort, genre, status string) string {
|
||||||
|
return BrowseFilteredHTMLKey(domain, page, sort, genre, status)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *HybridStore) BrowseCoverKey(domain, slug string) string {
|
func (h *HybridStore) BrowseCoverKey(domain, slug string) string {
|
||||||
return BrowseCoverKey(domain, slug)
|
return BrowseCoverKey(domain, slug)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,10 +223,33 @@ func (m *MinioClient) PresignAudio(ctx context.Context, key string, expires time
|
|||||||
|
|
||||||
// BrowseHTMLKey returns the MinIO object key for a SingleFile HTML snapshot.
|
// BrowseHTMLKey returns the MinIO object key for a SingleFile HTML snapshot.
|
||||||
// Layout: {domain}/html/page-{n}.html
|
// Layout: {domain}/html/page-{n}.html
|
||||||
|
// This uses the default (popular/all/all) filter combination.
|
||||||
func BrowseHTMLKey(domain string, page int) string {
|
func BrowseHTMLKey(domain string, page int) string {
|
||||||
return fmt.Sprintf("%s/html/page-%d.html", domain, page)
|
return fmt.Sprintf("%s/html/page-%d.html", domain, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BrowseFilteredHTMLKey returns the MinIO object key for a browse page snapshot
|
||||||
|
// that includes filter parameters (sort, genre, status) in the key so that
|
||||||
|
// different filter combinations are cached independently.
|
||||||
|
// Layout: {domain}/html/{sort}-{genre}-{status}/page-{n}.html
|
||||||
|
// Falls back to BrowseHTMLKey when all filters are at their default values
|
||||||
|
// (sort=popular, genre=all, status=all) for cache compatibility.
|
||||||
|
func BrowseFilteredHTMLKey(domain string, page int, sort, genre, status string) string {
|
||||||
|
if (sort == "" || sort == "popular") && (genre == "" || genre == "all") && (status == "" || status == "all") {
|
||||||
|
return BrowseHTMLKey(domain, page)
|
||||||
|
}
|
||||||
|
if sort == "" {
|
||||||
|
sort = "popular"
|
||||||
|
}
|
||||||
|
if genre == "" {
|
||||||
|
genre = "all"
|
||||||
|
}
|
||||||
|
if status == "" {
|
||||||
|
status = "all"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/html/%s-%s-%s/page-%d.html", domain, sort, genre, status, page)
|
||||||
|
}
|
||||||
|
|
||||||
// BrowseCoverKey returns the MinIO object key for a cached book cover image.
|
// BrowseCoverKey returns the MinIO object key for a cached book cover image.
|
||||||
// Layout: {domain}/assets/book-covers/{slug}.jpg
|
// Layout: {domain}/assets/book-covers/{slug}.jpg
|
||||||
func BrowseCoverKey(domain, slug string) string {
|
func BrowseCoverKey(domain, slug string) string {
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ type Store interface {
|
|||||||
// BrowseHTMLKey returns the MinIO object key for a SingleFile HTML snapshot.
|
// BrowseHTMLKey returns the MinIO object key for a SingleFile HTML snapshot.
|
||||||
// Layout: {domain}/html/page-{n}.html
|
// Layout: {domain}/html/page-{n}.html
|
||||||
BrowseHTMLKey(domain string, page int) string
|
BrowseHTMLKey(domain string, page int) string
|
||||||
|
// BrowseFilteredHTMLKey returns the MinIO object key for a browse page snapshot
|
||||||
|
// that incorporates sort/genre/status so different filter combos are cached separately.
|
||||||
|
BrowseFilteredHTMLKey(domain string, page int, sort, genre, status string) string
|
||||||
// BrowseCoverKey returns the MinIO object key for a cached book cover image.
|
// BrowseCoverKey returns the MinIO object key for a cached book cover image.
|
||||||
// Layout: {domain}/assets/book-covers/{slug}.jpg
|
// Layout: {domain}/assets/book-covers/{slug}.jpg
|
||||||
BrowseCoverKey(domain, slug string) string
|
BrowseCoverKey(domain, slug string) string
|
||||||
|
|||||||
Reference in New Issue
Block a user