The home timeline cache-busting calls (Cache::forget) are now
unconditional since caching is always on. Removed the config
checks from StatusObserver, FollowerObserver, and UnfollowPipeline.
Improvements to all three timeline services (Home, Public, Network):
1. DB fallback on cache exhaustion:
- Add fallbackMaxId() to each service that queries the DB when
Redis returns empty during max_id pagination
- Users can now scroll infinitely regardless of cache size
2. Cache TTL (24 hours):
- All sorted sets now expire after 24h, triggering a fresh rebuild
- Prevents stale data from accumulating
- Configurable via PF_HOME_TIMELINE_CACHE_TTL,
INSTANCE_PUBLIC_TIMELINE_CACHE_TTL, INSTANCE_NETWORK_TIMELINE_CACHE_TTL
3. Configurable dropoff limits:
- Home: 400 -> 800 (per user, via PF_HOME_TIMELINE_CACHE_DROPOFF)
- Public: 400 -> 10000 (via INSTANCE_PUBLIC_TIMELINE_CACHE_DROPOFF)
- Network: already 10000
4. Increased get() cap from 100 to 500 in all services
Remove the config entry and all DB-query fallback branches for the
network/federated timeline. NetworkTimelineService (Redis) is now
the only code path.
- config/instance.php: remove 'cached' key from timeline.network
- ApiV1Controller: remove DB fallback in network timeline branch
- PublicApiController: remove entire DB fallback (~70 lines)
- Helpers.php: remove cached check before adding to NetworkTimelineService
- Diagnostics: remove INSTANCE_NETWORK_TIMELINE_CACHED row
100 items was far too small — users hit an empty feed after just a
few pages since there's no DB fallback. 10000 gives deep scrolling
at negligible Redis cost (~80KB for status IDs in a sorted set).
When PF_NETWORK_TIMELINE is enabled (the default), the network/federated
timeline will now use NetworkTimelineService (Redis sorted set) by default
instead of direct DB queries.
The cache_dropoff (100) and max_hours_old (2160h/90d) tuning values
remain configurable via their respective env vars.
Remove the config entry and the DB-query fallback branch from
ApiV1Controller::timelinePublic(). The PublicTimelineService
(Redis sorted set) path is now the only code path for serving
the local public timeline via the Mastodon API.
This controls whether the Mastodon-compatible /api/v1/timelines/public
endpoint uses PublicTimelineService (Redis sorted set) or direct DB
queries. The Redis path is mature, handles cache warming, and Redis
is already a hard dependency. Defaulting to true for better performance
on all instances.
Remove EXP_CPT and EXP_CHT config entries from exp.php and eliminate
all conditional branches. The Redis-cached timeline paths are now the
only code paths:
- ApiV1Controller: Remove ~150 lines of DB-query home timeline fallback
- PublicApiController: Remove ~70 lines of DB-query public timeline fallback
- StatusObserver: Always dispatch feed removal on status delete
- StatusEntityLexer: Always dispatch feed insert on new status
- FeedRemoveDomainPipeline: Remove early-return guard
- Diagnostics: Remove EXP_CPT row
The cached timeline code is mature with proper cold-boot handling
and Redis is already a hard dependency for Pixelfed.
Change default for cached_public_timeline and cached_home_timeline
from false to true. Both use Redis sorted sets to serve timeline
feeds instead of direct DB queries on every request.
The code is mature with proper cold-boot handling (FeedWarmCachePipeline)
and full lifecycle management (insert/remove on post create/delete,
follow/unfollow, domain blocks). Redis is already a hard dependency.
Also remove a no-op frontend check in SPA Timeline.vue that set
baseApi to the same value regardless of the flag.