perf: eliminate full Sonarr Series + Radarr Movies library fetches
Build and Push Docker Image / build (push) Successful in 40s
Build and Push Docker Image / build (push) Successful in 40s
The poller was fetching the entire series and movie libraries on every poll cycle (~9s each). Queue and history records already embed the full series/movie object via includeSeries/includeMovie params. Changes: - Remove 'Sonarr Series' and 'Radarr Movies' timed fetches from poller - Tag queue/history records with _instanceUrl in the poller instead - Build seriesMap/moviesMap from embedded objects in dashboard - Remove poll:sonarr-series and poll:radarr-movies cache keys - Fix missing axios and config imports in dashboard - Net result: ~18s saved per poll cycle, ~2 fewer API calls
This commit is contained in:
+20
-14
@@ -1,9 +1,11 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
const { mapTorrentToDownload } = require('../utils/qbittorrent');
|
const { mapTorrentToDownload } = require('../utils/qbittorrent');
|
||||||
const cache = require('../utils/cache');
|
const cache = require('../utils/cache');
|
||||||
const { pollAllServices, getLastPollTimings, POLLING_ENABLED } = require('../utils/poller');
|
const { pollAllServices, getLastPollTimings, POLLING_ENABLED } = require('../utils/poller');
|
||||||
|
const { getSonarrInstances, getRadarrInstances } = require('../utils/config');
|
||||||
|
|
||||||
const EMBY_URL = process.env.EMBY_URL;
|
const EMBY_URL = process.env.EMBY_URL;
|
||||||
const EMBY_API_KEY = process.env.EMBY_API_KEY;
|
const EMBY_API_KEY = process.env.EMBY_API_KEY;
|
||||||
@@ -139,10 +141,8 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
const sabQueueData = cache.get('poll:sab-queue') || { slots: [] };
|
const sabQueueData = cache.get('poll:sab-queue') || { slots: [] };
|
||||||
const sabHistoryData = cache.get('poll:sab-history') || { slots: [] };
|
const sabHistoryData = cache.get('poll:sab-history') || { slots: [] };
|
||||||
const sonarrTagsResults = cache.get('poll:sonarr-tags') || [];
|
const sonarrTagsResults = cache.get('poll:sonarr-tags') || [];
|
||||||
const sonarrSeriesData = cache.get('poll:sonarr-series') || [];
|
|
||||||
const sonarrQueueData = cache.get('poll:sonarr-queue') || { records: [] };
|
const sonarrQueueData = cache.get('poll:sonarr-queue') || { records: [] };
|
||||||
const sonarrHistoryData = cache.get('poll:sonarr-history') || { records: [] };
|
const sonarrHistoryData = cache.get('poll:sonarr-history') || { records: [] };
|
||||||
const radarrMoviesData = cache.get('poll:radarr-movies') || [];
|
|
||||||
const radarrQueueData = cache.get('poll:radarr-queue') || { records: [] };
|
const radarrQueueData = cache.get('poll:radarr-queue') || { records: [] };
|
||||||
const radarrHistoryData = cache.get('poll:radarr-history') || { records: [] };
|
const radarrHistoryData = cache.get('poll:radarr-history') || { records: [] };
|
||||||
const radarrTagsData = cache.get('poll:radarr-tags') || [];
|
const radarrTagsData = cache.get('poll:radarr-tags') || [];
|
||||||
@@ -153,26 +153,32 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
const sabnzbdHistory = { data: { history: sabHistoryData } };
|
const sabnzbdHistory = { data: { history: sabHistoryData } };
|
||||||
const sonarrQueue = { data: sonarrQueueData };
|
const sonarrQueue = { data: sonarrQueueData };
|
||||||
const sonarrHistory = { data: sonarrHistoryData };
|
const sonarrHistory = { data: sonarrHistoryData };
|
||||||
const sonarrSeries = { data: sonarrSeriesData };
|
|
||||||
const radarrQueue = { data: radarrQueueData };
|
const radarrQueue = { data: radarrQueueData };
|
||||||
const radarrHistory = { data: radarrHistoryData };
|
const radarrHistory = { data: radarrHistoryData };
|
||||||
const radarrMovies = { data: radarrMoviesData };
|
|
||||||
const radarrTags = { data: radarrTagsData };
|
const radarrTags = { data: radarrTagsData };
|
||||||
|
|
||||||
console.log(`[Dashboard] Cache data - Series: ${sonarrSeries.data.length}, Movies: ${radarrMovies.data.length}, qBit: ${qbittorrentTorrents.length}`);
|
// Build series/movie maps from embedded objects in queue + history records
|
||||||
|
// (no need to fetch the full library — queue/history include the full object)
|
||||||
|
const seriesMap = new Map();
|
||||||
|
for (const r of sonarrQueue.data.records) {
|
||||||
|
if (r.series && r.seriesId) seriesMap.set(r.seriesId, r.series);
|
||||||
|
}
|
||||||
|
for (const r of sonarrHistory.data.records) {
|
||||||
|
if (r.series && r.seriesId && !seriesMap.has(r.seriesId)) seriesMap.set(r.seriesId, r.series);
|
||||||
|
}
|
||||||
|
const moviesMap = new Map();
|
||||||
|
for (const r of radarrQueue.data.records) {
|
||||||
|
if (r.movie && r.movieId) moviesMap.set(r.movieId, r.movie);
|
||||||
|
}
|
||||||
|
for (const r of radarrHistory.data.records) {
|
||||||
|
if (r.movie && r.movieId && !moviesMap.has(r.movieId)) moviesMap.set(r.movieId, r.movie);
|
||||||
|
}
|
||||||
|
|
||||||
// Create maps for quick lookup
|
|
||||||
const seriesMap = new Map(sonarrSeries.data.map(s => [s.id, s]));
|
|
||||||
const moviesMap = new Map(radarrMovies.data.map(m => [m.id, m]));
|
|
||||||
|
|
||||||
// Create tag maps (id -> label)
|
// Create tag maps (id -> label)
|
||||||
const sonarrTagMap = new Map(sonarrTagsResults.flatMap(t => t.data || []).map(t => [t.id, t.label]));
|
const sonarrTagMap = new Map(sonarrTagsResults.flatMap(t => t.data || []).map(t => [t.id, t.label]));
|
||||||
const radarrTagMap = new Map(radarrTags.data.map(t => [t.id, t.label]));
|
const radarrTagMap = new Map(radarrTags.data.map(t => [t.id, t.label]));
|
||||||
console.log(`[Dashboard] Radarr tags:`, JSON.stringify(radarrTags.data));
|
|
||||||
console.log(`[Dashboard] Movies map keys:`, Array.from(moviesMap.keys()).slice(0, 10));
|
console.log(`[Dashboard] Cache data - Series: ${seriesMap.size}, Movies: ${moviesMap.size}, qBit: ${qbittorrentTorrents.length}`);
|
||||||
console.log(`[Dashboard] Looking for movieId: 2962`);
|
|
||||||
console.log(`[Dashboard] Movie 2962:`, JSON.stringify(moviesMap.get(2962)));
|
|
||||||
console.log(`[Dashboard] Sample movie structure:`, JSON.stringify(radarrMovies.data[0]));
|
|
||||||
|
|
||||||
// Match SABnzbd downloads to Sonarr/Radarr activity
|
// Match SABnzbd downloads to Sonarr/Radarr activity
|
||||||
const userDownloads = [];
|
const userDownloads = [];
|
||||||
|
|||||||
+35
-30
@@ -80,14 +80,6 @@ async function pollAllServices() {
|
|||||||
return { instance: inst.id, data: { records: [] } };
|
return { instance: inst.id, data: { records: [] } };
|
||||||
})
|
})
|
||||||
))),
|
))),
|
||||||
timed('Sonarr Series', () => Promise.all(sonarrInstances.map(inst =>
|
|
||||||
axios.get(`${inst.url}/api/v3/series`, {
|
|
||||||
headers: { 'X-Api-Key': inst.apiKey }
|
|
||||||
}).then(res => ({ instance: inst.id, data: res.data })).catch(err => {
|
|
||||||
console.error(`[Poller] Sonarr ${inst.id} series error:`, err.message);
|
|
||||||
return { instance: inst.id, data: [] };
|
|
||||||
})
|
|
||||||
))),
|
|
||||||
timed('Radarr Queue', () => Promise.all(radarrInstances.map(inst =>
|
timed('Radarr Queue', () => Promise.all(radarrInstances.map(inst =>
|
||||||
axios.get(`${inst.url}/api/v3/queue`, {
|
axios.get(`${inst.url}/api/v3/queue`, {
|
||||||
headers: { 'X-Api-Key': inst.apiKey },
|
headers: { 'X-Api-Key': inst.apiKey },
|
||||||
@@ -106,14 +98,6 @@ async function pollAllServices() {
|
|||||||
return { instance: inst.id, data: { records: [] } };
|
return { instance: inst.id, data: { records: [] } };
|
||||||
})
|
})
|
||||||
))),
|
))),
|
||||||
timed('Radarr Movies', () => Promise.all(radarrInstances.map(inst =>
|
|
||||||
axios.get(`${inst.url}/api/v3/movie`, {
|
|
||||||
headers: { 'X-Api-Key': inst.apiKey }
|
|
||||||
}).then(res => ({ instance: inst.id, data: res.data })).catch(err => {
|
|
||||||
console.error(`[Poller] Radarr ${inst.id} movies error:`, err.message);
|
|
||||||
return { instance: inst.id, data: [] };
|
|
||||||
})
|
|
||||||
))),
|
|
||||||
timed('Radarr Tags', () => Promise.all(radarrInstances.map(inst =>
|
timed('Radarr Tags', () => Promise.all(radarrInstances.map(inst =>
|
||||||
axios.get(`${inst.url}/api/v3/tag`, {
|
axios.get(`${inst.url}/api/v3/tag`, {
|
||||||
headers: { 'X-Api-Key': inst.apiKey }
|
headers: { 'X-Api-Key': inst.apiKey }
|
||||||
@@ -131,9 +115,9 @@ async function pollAllServices() {
|
|||||||
const [
|
const [
|
||||||
{ result: sabQueues }, { result: sabHistories },
|
{ result: sabQueues }, { result: sabHistories },
|
||||||
{ result: sonarrTagsResults }, { result: sonarrQueues },
|
{ result: sonarrTagsResults }, { result: sonarrQueues },
|
||||||
{ result: sonarrHistories }, { result: sonarrSeriesResults },
|
{ result: sonarrHistories },
|
||||||
{ result: radarrQueues }, { result: radarrHistories },
|
{ result: radarrQueues }, { result: radarrHistories },
|
||||||
{ result: radarrMoviesResults }, { result: radarrTagsResults },
|
{ result: radarrTagsResults },
|
||||||
{ result: qbittorrentTorrents }
|
{ result: qbittorrentTorrents }
|
||||||
] = results;
|
] = results;
|
||||||
|
|
||||||
@@ -164,28 +148,49 @@ async function pollAllServices() {
|
|||||||
|
|
||||||
// Sonarr
|
// Sonarr
|
||||||
cache.set('poll:sonarr-tags', sonarrTagsResults, cacheTTL);
|
cache.set('poll:sonarr-tags', sonarrTagsResults, cacheTTL);
|
||||||
|
// Tag queue/history records with _instanceUrl so embedded series/movie objects can build links
|
||||||
cache.set('poll:sonarr-queue', {
|
cache.set('poll:sonarr-queue', {
|
||||||
records: sonarrQueues.flatMap(q => q.data.records || [])
|
records: sonarrQueues.flatMap(q => {
|
||||||
|
const inst = sonarrInstances.find(i => i.id === q.instance);
|
||||||
|
const url = inst ? inst.url : null;
|
||||||
|
return (q.data.records || []).map(r => {
|
||||||
|
if (r.series) r.series._instanceUrl = url;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
})
|
||||||
}, cacheTTL);
|
}, cacheTTL);
|
||||||
cache.set('poll:sonarr-history', {
|
cache.set('poll:sonarr-history', {
|
||||||
records: sonarrHistories.flatMap(h => h.data.records || [])
|
records: sonarrHistories.flatMap(h => {
|
||||||
|
const inst = sonarrInstances.find(i => i.id === h.instance);
|
||||||
|
const url = inst ? inst.url : null;
|
||||||
|
return (h.data.records || []).map(r => {
|
||||||
|
if (r.series) r.series._instanceUrl = url;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
})
|
||||||
}, cacheTTL);
|
}, cacheTTL);
|
||||||
cache.set('poll:sonarr-series', sonarrSeriesResults.flatMap(s => {
|
|
||||||
const inst = sonarrInstances.find(i => i.id === s.instance);
|
|
||||||
return (s.data || []).map(item => ({ ...item, _instanceUrl: inst ? inst.url : null }));
|
|
||||||
}), cacheTTL);
|
|
||||||
|
|
||||||
// Radarr
|
// Radarr
|
||||||
cache.set('poll:radarr-queue', {
|
cache.set('poll:radarr-queue', {
|
||||||
records: radarrQueues.flatMap(q => q.data.records || [])
|
records: radarrQueues.flatMap(q => {
|
||||||
|
const inst = radarrInstances.find(i => i.id === q.instance);
|
||||||
|
const url = inst ? inst.url : null;
|
||||||
|
return (q.data.records || []).map(r => {
|
||||||
|
if (r.movie) r.movie._instanceUrl = url;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
})
|
||||||
}, cacheTTL);
|
}, cacheTTL);
|
||||||
cache.set('poll:radarr-history', {
|
cache.set('poll:radarr-history', {
|
||||||
records: radarrHistories.flatMap(h => h.data.records || [])
|
records: radarrHistories.flatMap(h => {
|
||||||
|
const inst = radarrInstances.find(i => i.id === h.instance);
|
||||||
|
const url = inst ? inst.url : null;
|
||||||
|
return (h.data.records || []).map(r => {
|
||||||
|
if (r.movie) r.movie._instanceUrl = url;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
})
|
||||||
}, cacheTTL);
|
}, cacheTTL);
|
||||||
cache.set('poll:radarr-movies', radarrMoviesResults.flatMap(m => {
|
|
||||||
const inst = radarrInstances.find(i => i.id === m.instance);
|
|
||||||
return (m.data || []).map(item => ({ ...item, _instanceUrl: inst ? inst.url : null }));
|
|
||||||
}), cacheTTL);
|
|
||||||
cache.set('poll:radarr-tags', radarrTagsResults.flatMap(t => t.data || []), cacheTTL);
|
cache.set('poll:radarr-tags', radarrTagsResults.flatMap(t => t.data || []), cacheTTL);
|
||||||
|
|
||||||
// qBittorrent
|
// qBittorrent
|
||||||
|
|||||||
Reference in New Issue
Block a user