What problem are you trying to solve?
Lib/src/main/java/me/egg82/arr/radarr/v3/schema/MovieResource.java:324
public @NotNull PVector<@NotNull Tag> tags() {
List<@NotNull Tag> r = new ArrayList<>();
for (int id : this.tags) {
Tag t = api.fetch(Tag.class, id);
Every call does an api.fetch per tag. Same shape at SeriesResource.java:298, ArtistResource.java:251 and Author.java:122.
This only bites when SKIP_TAGS is set. All five updaters call it from exactly one place, gated:
if (!skipTags.isEmpty() && hasAnyTag(skipTags, m.resource().tags())) {
RadarrUpdater.java:133, SonarrUpdater.java:106, LidarrUpdater.java:103, ReadarrUpdater.java:109, WhisparrUpdater.java:133. With no SKIP_TAGS configured the short-circuit means tags() is never called at all - so this costs nothing on a default install and scales with items × tags on one that uses the feature. Measured at 30 ms/cycle at 3 tags/item, 70 ms at 9.
The part that makes it worse
fetch(Class, int, params) skips its own in-memory idCache under the default configuration. AbstractArrAPI.java:118-131:
if ((memoryCache == Tristate.AUTO && fileCache == Tristate.AUTO && !isCacheWritable())
|| (memoryCache == Tristate.AUTO && fileCache == Tristate.FALSE)
|| memoryCache == Tristate.TRUE) {
// ... idCache.computeIfAbsent(...)
}
return fetchInternal(type, id, params); // idCache bypassed
USE_FILE_CACHE and USE_MEMORY_CACHE both default to AUTO, so with a writable cache directory the whole condition is false and the memory cache is never consulted - every tag lookup goes to fetchInternal, which reads and parses files from disk. A tag referenced by twenty items in one cycle is read twenty times.
What would you like Fetcharr to do?
A method named like an accessor returns data the object already has, and a repeated lookup of the same small object is served from memory.
Resolve the relation at the boundary rather than in the DTO: fetch the tag list once per cycle, build a label to id map, and compare ints against the IntSet already on the resource. hasAnyTag only needs the comparison, not hydrated Tag objects.
The idCache bypass is worth looking at independently of that - the condition reads as though it were meant to enable the memory cache when the file cache is unavailable, and instead disables it whenever the file cache works.
More broadly, a DTO holding a reference to ArrAPI is what makes an accessor able to do file reads at all.
What problem are you trying to solve?
Lib/src/main/java/me/egg82/arr/radarr/v3/schema/MovieResource.java:324Every call does an
api.fetchper tag. Same shape atSeriesResource.java:298,ArtistResource.java:251andAuthor.java:122.This only bites when
SKIP_TAGSis set. All five updaters call it from exactly one place, gated:RadarrUpdater.java:133,SonarrUpdater.java:106,LidarrUpdater.java:103,ReadarrUpdater.java:109,WhisparrUpdater.java:133. With noSKIP_TAGSconfigured the short-circuit meanstags()is never called at all - so this costs nothing on a default install and scales with items × tags on one that uses the feature. Measured at 30 ms/cycle at 3 tags/item, 70 ms at 9.The part that makes it worse
fetch(Class, int, params)skips its own in-memoryidCacheunder the default configuration.AbstractArrAPI.java:118-131:USE_FILE_CACHEandUSE_MEMORY_CACHEboth default toAUTO, so with a writable cache directory the whole condition is false and the memory cache is never consulted - every tag lookup goes tofetchInternal, which reads and parses files from disk. A tag referenced by twenty items in one cycle is read twenty times.What would you like Fetcharr to do?
A method named like an accessor returns data the object already has, and a repeated lookup of the same small object is served from memory.
Resolve the relation at the boundary rather than in the DTO: fetch the tag list once per cycle, build a label to id map, and compare ints against the
IntSetalready on the resource.hasAnyTagonly needs the comparison, not hydratedTagobjects.The idCache bypass is worth looking at independently of that - the condition reads as though it were meant to enable the memory cache when the file cache is unavailable, and instead disables it whenever the file cache works.
More broadly, a DTO holding a reference to
ArrAPIis what makes an accessor able to do file reads at all.