Skip to content

fix(arr): make arr_id_cache expiry timezone-independent - #1986

Merged
s0up4200 merged 1 commit into
developfrom
fix/1961-arr-cache-utc
Jun 8, 2026
Merged

fix(arr): make arr_id_cache expiry timezone-independent#1986
s0up4200 merged 1 commit into
developfrom
fix/1961-arr-cache-utc

Conversation

@nitrobass24

@nitrobass24 nitrobass24 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

ArrIDCacheStore's cache was silently disabled for anyone running qui in a non-UTC timezone. Set stored expires_at as a local-time time.Time, while Get / CleanupExpired / CountValid compared it against SQLite's CURRENT_TIMESTAMP (UTC). The modernc.org/sqlite driver serializes the bound time.Time as a string whose lexical ordering against the CURRENT_TIMESTAMP UTC string only matches chronological order when the process runs in UTC. In any other timezone, Get returned sql.ErrNoRows for a freshly written, unexpired entry — so every ARR title lookup hit the upstream HTTP endpoint instead of the cache. CI never caught it because GitHub runners default to TZ=UTC.

Fix

Make both sides of every expiry comparison flow through the same driver path as UTC time.Time values:

  • SetWithTitles stores time.Now().Add(ttl).UTC().
  • Get / CleanupExpired / CountValid compare expires_at > ? (and <= ?) binding time.Now().UTC() instead of CURRENT_TIMESTAMP.

This is dialect-agnostic — the ? placeholder is rebound to $N for Postgres — and needs no migration. It also removes a latent timestamp vs timestamptz coercion that CURRENT_TIMESTAMP caused on Postgres. cached_at is unchanged (it is never used in a comparison).

The sibling torznab_search_cache store uses the same expires_at > CURRENT_TIMESTAMP SQL but is already safe because it stores expires_at in UTC, so it is intentionally left untouched.

Upgrade note

Pre-fix rows written with a local-time expires_at are treated as expired after upgrading. This is a benign one-time cache miss (the entry is re-fetched), not stale or wrong data — no migration required.

Testing

  • go test -race -count=1 ./internal/models/ ./internal/services/arr/ passes under both the default TZ and TZ=UTC.
  • The two pre-existing arr service tests that failed outside UTC (TestService_LookupExternalIDsUsesNegativeCache, ...KeepsLegacyPositiveCacheWhenAliasHydrationMisses) now pass under TZ=America/Chicago.
  • New regression test internal/models/arr_id_cache_test.go pins time.Local to a fixed non-UTC zone so it reproduces on UTC CI runners — verified to fail before this change (sql: no rows in result set) and pass after. Covers positive + negative entries, the expired-entry-unretrievable path, and CountValid/CleanupExpired.
  • gofmt, golangci-lint, go vet clean on the changed files; make build succeeds.

Follow-up (not in this PR)

The issue suggested running the suite under a non-UTC TZ in CI to catch this class of bug. This PR's regression test already reproduces it under UTC, but a dedicated non-UTC CI job remains a reasonable separate addition.

Closes #1961

Summary by CodeRabbit

  • Bug Fixes

    • Fixed cache expiry handling to work correctly regardless of system timezone settings, ensuring consistent behavior across different server environments.
  • Tests

    • Added timezone-specific tests to verify cache operations function properly when the system timezone is non-UTC.

ArrIDCacheStore stored expires_at as a local-time value but compared it
against CURRENT_TIMESTAMP (UTC). The modernc.org/sqlite driver serializes the
bound time.Time as a string whose lexical ordering against the UTC
CURRENT_TIMESTAMP string only matches chronological order when the process runs
in UTC. In any other timezone Get returned sql.ErrNoRows for a freshly written,
unexpired entry, silently disabling the cache so every ARR title lookup hit the
upstream HTTP endpoint. CI never caught it because runners default to UTC.

Store expires_at in UTC and compare against a bound time.Now().UTC() parameter
in Get, CleanupExpired, and CountValid instead of CURRENT_TIMESTAMP. Both sides
now flow through the same driver path, making the comparison timezone-independent
on SQLite and Postgres alike (the ? placeholder is rebound to $N for Postgres).
No migration is required; pre-fix rows with a local-time expires_at are treated
as expired after upgrade, a benign one-time cache miss.

Add a regression test that pins time.Local to a non-UTC zone so it reproduces on
UTC CI runners; it fails before this change and passes after.

Closes #1961
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e9abd6a5-f370-4f6c-bb87-ac8eece4f8e2

📥 Commits

Reviewing files that changed from the base of the PR and between 719866b and f0d1dbe.

📒 Files selected for processing (2)
  • internal/models/arr_id_cache.go
  • internal/models/arr_id_cache_test.go

Walkthrough

The PR fixes a timezone-dependent bug in ArrIDCacheStore where cache lookups fail in non-UTC environments. Implementation switches from SQL CURRENT_TIMESTAMP to parameterized UTC time binding in Get, SetWithTitles, CleanupExpired, and CountValid. New regression tests verify correct behavior under non-UTC time.Local.

Changes

Timezone-Independent Cache Expiry

Layer / File(s) Summary
Timezone-independent expiry implementation
internal/models/arr_id_cache.go
Get, CleanupExpired, CountValid, and SetWithTitles are updated to use UTC-bound parameterized time values instead of SQL CURRENT_TIMESTAMP, ensuring lexical string comparisons behave chronologically regardless of process timezone.
Non-UTC timezone regression tests
internal/models/arr_id_cache_test.go
Introduces forceNonUTCLocal helper and three regression tests that verify Get, SetWithTitles, CleanupExpired, CountValid, and negative cache entries all behave correctly when time.Local is non-UTC.

🎯 2 (Simple) | ⏱️ ~12 minutes

🐰 A cache once lost in timezone's night,
Now bound to UTC, shining bright,
In Central, Pacific, or Eastern zone,
The entries are found, never alone!
Tests guard against the shadow of doubt.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(arr): make arr_id_cache expiry timezone-independent' accurately and concisely summarizes the main change—resolving a timezone bug in cache expiry logic.
Linked Issues check ✅ Passed All coding objectives from issue #1961 are met: timezone-independent expiry logic, bound UTC parameters instead of CURRENT_TIMESTAMP, UTC normalization in Set, regression tests with non-UTC timezone forcing, and no DB migration required.
Out of Scope Changes check ✅ Passed All changes are scoped to the arr_id_cache expiry timezone fix: modifications to Get/Set/CleanupExpired/CountValid and new regression tests for non-UTC behavior; no unrelated changes present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1961-arr-cache-utc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nitrobass24 nitrobass24 self-assigned this Jun 2, 2026
@nitrobass24 nitrobass24 added this to the v1.20.0 milestone Jun 2, 2026
@nitrobass24
nitrobass24 requested a review from s0up4200 June 2, 2026 19:18
@s0up4200
s0up4200 merged commit 255bb3e into develop Jun 8, 2026
13 checks passed
@s0up4200
s0up4200 deleted the fix/1961-arr-cache-utc branch June 8, 2026 08:02
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request Jun 9, 2026
… (#950)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.19.0` → `v1.20.0` |

---

### Release Notes

<details>
<summary>autobrr/qui (ghcr.io/autobrr/qui)</summary>

### [`v1.20.0`](https://github.com/autobrr/qui/releases/tag/v1.20.0)

[Compare Source](autobrr/qui@v1.19.0...v1.20.0)

##### Changelog

##### New Features

- [`9ad7ffe`](autobrr/qui@9ad7ffe): feat(automations): add include/exclude for trackers ([#&#8203;1953](autobrr/qui#1953)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`e73867f`](autobrr/qui@e73867f): feat(ci): build binaries for develop branch ([#&#8203;1969](autobrr/qui#1969)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`2020847`](autobrr/qui@2020847): feat(crossseed): match cross-tracker relabels and alternate and/& title spellings ([#&#8203;1990](autobrr/qui#1990)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`719866b`](autobrr/qui@719866b): feat(crossseed): route season packs to categories by resolution and source ([#&#8203;1972](autobrr/qui#1972)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`a6e22ba`](autobrr/qui@a6e22ba): feat(i18n): add French translation, dynamic loading, and browser locale auto-detect ([#&#8203;1956](autobrr/qui#1956)) ([@&#8203;OlziYT](https://github.com/OlziYT))
- [`77c6ea8`](autobrr/qui@77c6ea8): feat(i18n): add German (de) translation ([#&#8203;1965](autobrr/qui#1965)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`fc8664b`](autobrr/qui@fc8664b): feat(i18n): add multi-language support with Simplified Chinese ([#&#8203;1732](autobrr/qui#1732)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`11ace50`](autobrr/qui@11ace50): feat(sse): stream torrent and activity updates, replacing background polling ([#&#8203;551](autobrr/qui#551)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`9b7ed6c`](autobrr/qui@9b7ed6c): feat(torrents): filter for torrents with other tracker errors ([#&#8203;1945](autobrr/qui#1945)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`62bbdb4`](autobrr/qui@62bbdb4): feat(web): virtualized and memoized the indexer search results  ([#&#8203;1944](autobrr/qui#1944)) ([@&#8203;GtwoK](https://github.com/GtwoK))

##### Bug Fixes

- [`b10ef98`](autobrr/qui@b10ef98): fix(api): remove WriteTimeout that aborted downloads and SSE at 120s ([#&#8203;1964](autobrr/qui#1964)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`255bb3e`](autobrr/qui@255bb3e): fix(arr): make arr\_id\_cache expiry timezone-independent ([#&#8203;1986](autobrr/qui#1986)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`4c1d743`](autobrr/qui@4c1d743): fix(crossseed): harden filesystem fallback and post-add rechecks ([#&#8203;1912](autobrr/qui#1912)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`8b278e5`](autobrr/qui@8b278e5): fix(crossseed): pin explicit savepath when cross category save path diverges ([#&#8203;1993](autobrr/qui#1993)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`cab1e0f`](autobrr/qui@cab1e0f): fix(gazelle): pre-search content matching and api limits ([#&#8203;1981](autobrr/qui#1981)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`c3fa169`](autobrr/qui@c3fa169): fix(i18n): translate torrent state labels ([#&#8203;1955](autobrr/qui#1955)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`ccb601d`](autobrr/qui@ccb601d): fix(proxy): forward API key auth to qBittorrent on passthrough requests ([#&#8203;1948](autobrr/qui#1948)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`3cba046`](autobrr/qui@3cba046): fix(qbittorrent): match 'repack available'/'grab internal' as unregistered ([#&#8203;1960](autobrr/qui#1960)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`591941a`](autobrr/qui@591941a): fix(sse): pause torrent-list stream while the tab is hidden ([#&#8203;1994](autobrr/qui#1994)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`f75a1eb`](autobrr/qui@f75a1eb): fix(sse): stop intermittent "offline" flips that fall back to polling ([#&#8203;1992](autobrr/qui#1992)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`c720c39`](autobrr/qui@c720c39): fix(web): emit allowSubcategories from the filtered-data callback ([#&#8203;1979](autobrr/qui#1979)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`602d440`](autobrr/qui@602d440): fix(web): preserve paginated rows on unified-view SSE updates ([#&#8203;1985](autobrr/qui#1985)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))

##### Other Changes

- [`1006338`](autobrr/qui@1006338): chore(deps): bump actions/checkout from 6 to 6.0.2 in the github group ([#&#8203;1999](autobrr/qui#1999)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`667d654`](autobrr/qui@667d654): chore(deps): bump rls to v0.8.1 ([#&#8203;2007](autobrr/qui#2007)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`cc7d70a`](autobrr/qui@cc7d70a): chore(deps): bump the github group with 5 updates ([#&#8203;1937](autobrr/qui#1937)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`cc49a49`](autobrr/qui@cc49a49): chore(deps): bump the github group with 5 updates ([#&#8203;1959](autobrr/qui#1959)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`eff1a65`](autobrr/qui@eff1a65): chore(deps): bump the golang group with 14 updates ([#&#8203;1988](autobrr/qui#1988)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`11da2c7`](autobrr/qui@11da2c7): chore(deps): bump the npm group in /web with 11 updates ([#&#8203;1989](autobrr/qui#1989)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`33aad41`](autobrr/qui@33aad41): chore(deps): bump the npm group in /web with 28 updates ([#&#8203;1946](autobrr/qui#1946)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`5c74fcf`](autobrr/qui@5c74fcf): chore(refactor): remove explicit any and enable no-explicit-any rule ([#&#8203;1966](autobrr/qui#1966)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`fefa624`](autobrr/qui@fefa624): chore(web): add vitest test infrastructure and CI gating ([#&#8203;1936](autobrr/qui#1936)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`90b573a`](autobrr/qui@90b573a): chore(web): remove unused code and packages ([#&#8203;1957](autobrr/qui#1957)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`078fae9`](autobrr/qui@078fae9): docs(agents): scope agent guidance ([#&#8203;1995](autobrr/qui#1995)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`264ccfb`](autobrr/qui@264ccfb): refactor(go): remove dead code ([#&#8203;1740](autobrr/qui#1740)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`48d6f57`](autobrr/qui@48d6f57): refactor(web): extract TorrentTableDialogs from TorrentTableOptimized ([#&#8203;1980](autobrr/qui#1980)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`022f553`](autobrr/qui@022f553): refactor(web): extract compact-sort, cross-seed, and hotkey hooks ([#&#8203;1976](autobrr/qui#1976)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`298c78c`](autobrr/qui@298c78c): refactor(web): extract torrent-table display helpers and row components ([#&#8203;1970](autobrr/qui#1970)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`a5b741a`](autobrr/qui@a5b741a): refactor(web): extract useBulkActionWrappers ([#&#8203;1977](autobrr/qui#1977)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`d4a051e`](autobrr/qui@d4a051e): refactor(web): extract useTorrentSelection ([#&#8203;1974](autobrr/qui#1974)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`cf84390`](autobrr/qui@cf84390): refactor(web): extract useTorrentSelectionDerivations ([#&#8203;1975](autobrr/qui#1975)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`18adff9`](autobrr/qui@18adff9): refactor(web): extract useTorrentTableColumns and useTorrentTableNotifications ([#&#8203;1978](autobrr/qui#1978)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`eb336ac`](autobrr/qui@eb336ac): refactor(web): extract useTorrentTableFilterExpr ([#&#8203;1973](autobrr/qui#1973)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`f1556e8`](autobrr/qui@f1556e8): refactor(web): extract useTrackerIconCache and useEffectiveServerState ([#&#8203;1971](autobrr/qui#1971)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`31f2cd9`](autobrr/qui@31f2cd9): refactor(web): extract virtualization, filter-lifecycle FSM, and column DnD from TorrentTableOptimized ([#&#8203;1982](autobrr/qui#1982)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`fdd0e56`](autobrr/qui@fdd0e56): refactor(web): split types/index.ts into domain modules behind a barrel ([#&#8203;1968](autobrr/qui#1968)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`2cca076`](autobrr/qui@2cca076): test(db): speed up migrated database tests ([#&#8203;1938](autobrr/qui#1938)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`a10e697`](autobrr/qui@a10e697): test(sse): warm up subscription before coalescing burst ([#&#8203;1987](autobrr/qui#1987)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`05fda2a`](autobrr/qui@05fda2a): test(web): add unit tests for Priority 1 pure utilities ([#&#8203;1940](autobrr/qui#1940)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`8df70a5`](autobrr/qui@8df70a5): test: fan out testdb helper to remaining database.New callers ([#&#8203;1962](autobrr/qui#1962)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))

**Full Changelog**: <autobrr/qui@v1.19.0...v1.20.0>

##### Docker images

- `docker pull ghcr.io/autobrr/qui:v1.20.0`
- `docker pull ghcr.io/autobrr/qui:latest`

##### What to do next?

- Join our [Discord server](https://discord.autobrr.com/qui)

Thank you for using qui!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/950
hbjydev pushed a commit to hbjydev/phoebe that referenced this pull request Jun 10, 2026
… ) (#78)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.19.0` → `v1.20.0` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the Dependency Dashboard for more information.

---

### Release Notes

<details>
<summary>autobrr/qui (ghcr.io/autobrr/qui)</summary>

### [`v1.20.0`](https://github.com/autobrr/qui/releases/tag/v1.20.0)

[Compare Source](autobrr/qui@v1.19.0...v1.20.0)

##### Changelog

##### New Features

- [`9ad7ffe`](autobrr/qui@9ad7ffe): feat(automations): add include/exclude for trackers ([#&#8203;1953](autobrr/qui#1953)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`e73867f`](autobrr/qui@e73867f): feat(ci): build binaries for develop branch ([#&#8203;1969](autobrr/qui#1969)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`2020847`](autobrr/qui@2020847): feat(crossseed): match cross-tracker relabels and alternate and/& title spellings ([#&#8203;1990](autobrr/qui#1990)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`719866b`](autobrr/qui@719866b): feat(crossseed): route season packs to categories by resolution and source ([#&#8203;1972](autobrr/qui#1972)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`a6e22ba`](autobrr/qui@a6e22ba): feat(i18n): add French translation, dynamic loading, and browser locale auto-detect ([#&#8203;1956](autobrr/qui#1956)) ([@&#8203;OlziYT](https://github.com/OlziYT))
- [`77c6ea8`](autobrr/qui@77c6ea8): feat(i18n): add German (de) translation ([#&#8203;1965](autobrr/qui#1965)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`fc8664b`](autobrr/qui@fc8664b): feat(i18n): add multi-language support with Simplified Chinese ([#&#8203;1732](autobrr/qui#1732)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`11ace50`](autobrr/qui@11ace50): feat(sse): stream torrent and activity updates, replacing background polling ([#&#8203;551](autobrr/qui#551)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`9b7ed6c`](autobrr/qui@9b7ed6c): feat(torrents): filter for torrents with other tracker errors ([#&#8203;1945](autobrr/qui#1945)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`62bbdb4`](autobrr/qui@62bbdb4): feat(web): virtualized and memoized the indexer search results  ([#&#8203;1944](autobrr/qui#1944)) ([@&#8203;GtwoK](https://github.com/GtwoK))

##### Bug Fixes

- [`b10ef98`](autobrr/qui@b10ef98): fix(api): remove WriteTimeout that aborted downloads and SSE at 120s ([#&#8203;1964](autobrr/qui#1964)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`255bb3e`](autobrr/qui@255bb3e): fix(arr): make arr\_id\_cache expiry timezone-independent ([#&#8203;1986](autobrr/qui#1986)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`4c1d743`](autobrr/qui@4c1d743): fix(crossseed): harden filesystem fallback and post-add rechecks ([#&#8203;1912](autobrr/qui#1912)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`8b278e5`](autobrr/qui@8b278e5): fix(crossseed): pin explicit savepath when cross category save path diverges ([#&#8203;1993](autobrr/qui#1993)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`cab1e0f`](autobrr/qui@cab1e0f): fix(gazelle): pre-search content matching and api limits ([#&#8203;1981](autobrr/qui#1981)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`c3fa169`](autobrr/qui@c3fa169): fix(i18n): translate torrent state labels ([#&#8203;1955](autobrr/qui#1955)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`ccb601d`](autobrr/qui@ccb601d): fix(proxy): forward API key auth to qBittorrent on passthrough requests ([#&#8203;1948](autobrr/qui#1948)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`3cba046`](autobrr/qui@3cba046): fix(qbittorrent): match 'repack available'/'grab internal' as unregistered ([#&#8203;1960](autobrr/qui#1960)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`591941a`](autobrr/qui@591941a): fix(sse): pause torrent-list stream while the tab is hidden ([#&#8203;1994](autobrr/qui#1994)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`f75a1eb`](autobrr/qui@f75a1eb): fix(sse): stop intermittent "offline" flips that fall back to polling ([#&#8203;1992](autobrr/qui#1992)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`c720c39`](autobrr/qui@c720c39): fix(web): emit allowSubcategories from the filtered-data callback ([#&#8203;1979](autobrr/qui#1979)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`602d440`](autobrr/qui@602d440): fix(web): preserve paginated rows on unified-view SSE updates ([#&#8203;1985](autobrr/qui#1985)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))

##### Other Changes

- [`1006338`](autobrr/qui@1006338): chore(deps): bump actions/checkout from 6 to 6.0.2 in the github group ([#&#8203;1999](autobrr/qui#1999)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`667d654`](autobrr/qui@667d654): chore(deps): bump rls to v0.8.1 ([#&#8203;2007](autobrr/qui#2007)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`cc7d70a`](autobrr/qui@cc7d70a): chore(deps): bump the github group with 5 updates ([#&#8203;1937](autobrr/qui#1937)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`cc49a49`](autobrr/qui@cc49a49): chore(deps): bump the github group with 5 updates ([#&#8203;1959](autobrr/qui#1959)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`eff1a65`](autobrr/qui@eff1a65): chore(deps): bump the golang group with 14 updates ([#&#8203;1988](autobrr/qui#1988)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`11da2c7`](autobrr/qui@11da2c7): chore(deps): bump the npm group in /web with 11 updates ([#&#8203;1989](autobrr/qui#1989)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`33aad41`](autobrr/qui@33aad41): chore(deps): bump the npm group in /web with 28 updates ([#&#8203;1946](autobrr/qui#1946)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`5c74fcf`](autobrr/qui@5c74fcf): chore(refactor): remove explicit any and enable no-explicit-any rule ([#&#8203;1966](autobrr/qui#1966)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`fefa624`](autobrr/qui@fefa624): chore(web): add vitest test infrastructure and CI gating ([#&#8203;1936](autobrr/qui#1936)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`90b573a`](autobrr/qui@90b573a): chore(web): remove unused code and packages ([#&#8203;1957](autobrr/qui#1957)) ([@&#8203;martylukyy](https://github.com/martylukyy))
- [`078fae9`](autobrr/qui@078fae9): docs(agents): scope agent guidance ([#&#8203;1995](autobrr/qui#1995)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`264ccfb`](autobrr/qui@264ccfb): refactor(go): remove dead code ([#&#8203;1740](autobrr/qui#1740)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`48d6f57`](autobrr/qui@48d6f57): refactor(web): extract TorrentTableDialogs from TorrentTableOptimized ([#&#8203;1980](autobrr/qui#1980)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`022f553`](autobrr/qui@022f553): refactor(web): extract compact-sort, cross-seed, and hotkey hooks ([#&#8203;1976](autobrr/qui#1976)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`298c78c`](autobrr/qui@298c78c): refactor(web): extract torrent-table display helpers and row components ([#&#8203;1970](autobrr/qui#1970)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`a5b741a`](autobrr/qui@a5b741a): refactor(web): extract useBulkActionWrappers ([#&#8203;1977](autobrr/qui#1977)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`d4a051e`](autobrr/qui@d4a051e): refactor(web): extract useTorrentSelection ([#&#8203;1974](autobrr/qui#1974)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`cf84390`](autobrr/qui@cf84390): refactor(web): extract useTorrentSelectionDerivations ([#&#8203;1975](autobrr/qui#1975)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`18adff9`](autobrr/qui@18adff9): refactor(web): extract useTorrentTableColumns and useTorrentTableNotifications ([#&#8203;1978](autobrr/qui#1978)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`eb336ac`](autobrr/qui@eb336ac): refactor(web): extract useTorrentTableFilterExpr ([#&#8203;1973](autobrr/qui#1973)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`f1556e8`](autobrr/qui@f1556e8): refactor(web): extract useTrackerIconCache and useEffectiveServerState ([#&#8203;1971](autobrr/qui#1971)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`31f2cd9`](autobrr/qui@31f2cd9): refactor(web): extract virtualization, filter-lifecycle FSM, and column DnD from TorrentTableOptimized ([#&#8203;1982](autobrr/qui#1982)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`fdd0e56`](autobrr/qui@fdd0e56): refactor(web): split types/index.ts into domain modules behind a barrel ([#&#8203;1968](autobrr/qui#1968)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`2cca076`](autobrr/qui@2cca076): test(db): speed up migrated database tests ([#&#8203;1938](autobrr/qui#1938)) ([@&#8203;Audionut](https://github.com/Audionut))
- [`a10e697`](autobrr/qui@a10e697): test(sse): warm up subscription before coalescing burst ([#&#8203;1987](autobrr/qui#1987)) ([@&#8203;s0up4200](https://github.com/s0up4200))
- [`05fda2a`](autobrr/qui@05fda2a): test(web): add unit tests for Priority 1 pure utilities ([#&#8203;1940](autobrr/qui#1940)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))
- [`8df70a5`](autobrr/qui@8df70a5): test: fan out testdb helper to remaining database.New callers ([#&#8203;1962](autobrr/qui#1962)) ([@&#8203;nitrobass24](https://github.com/nitrobass24))

**Full Changelog**: <autobrr/qui@v1.19.0...v1.20.0>

##### Docker images

- `docker pull ghcr.io/autobrr/qui:v1.20.0`
- `docker pull ghcr.io/autobrr/qui:latest`

##### What to do next?

- Join our [Discord server](https://discord.autobrr.com/qui)

Thank you for using qui!

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/London)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://forgejo.hayden.moe/hayden/phoebe/pulls/78
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(arr): ArrIDCacheStore cache invisible in non-UTC timezones

2 participants