feat: improve listens report response time

Reduce response time for larger intervals (e.g. all time) from 9 seconds
to 150ms in my tests.
This commit is contained in:
Julian Tölle 2023-09-16 23:14:35 +02:00
parent 26087e4e6d
commit 89440daf7b
2 changed files with 19 additions and 13 deletions

View file

@ -77,7 +77,7 @@ export const ReportListens: React.FC = () => {
{isLoading && <Spinner className="m-8" />} {isLoading && <Spinner className="m-8" />}
{!reportHasItems && !isLoading && ( {!reportHasItems && !isLoading && (
<div> <div>
<p>Report is emtpy! :(</p> <p>Report is empty! :(</p>
</div> </div>
)} )}
{reportHasItems && ( {reportHasItems && (
@ -128,7 +128,7 @@ const ReportGraph: React.FC<{
<AreaChart <AreaChart
data={dataLocal} data={dataLocal}
margin={{ margin={{
left: -20, left: -5,
}} }}
> >
<defs> <defs>

View file

@ -10,7 +10,6 @@ import {
isSameMonth, isSameMonth,
isSameWeek, isSameWeek,
isSameYear, isSameYear,
min,
parseJSON, parseJSON,
startOfDay, startOfDay,
sub, sub,
@ -73,7 +72,13 @@ export class ReportsService {
async getListens(options: GetListenReportDto): Promise<ListenReportDto> { async getListens(options: GetListenReportDto): Promise<ListenReportDto> {
const { timeFrame, time: timePreset } = options; const { timeFrame, time: timePreset } = options;
const listens = await this.getListensQueryFromOptions(options).getMany(); const results = await this.getListensQueryFromOptions(options)
.select(`count(*) as "listenCount"`)
.addSelect(`date_trunc(:timeFrame, "playedAt") as date`)
.groupBy("date")
.orderBy("date")
.setParameter("timeFrame", timeFrame)
.getRawMany<{ listenCount: string; date: Date }>();
const interval = this.getIntervalFromPreset(timePreset); const interval = this.getIntervalFromPreset(timePreset);
const { eachOfInterval, isSame } = timeframeToDateFns[timeFrame]; const { eachOfInterval, isSame } = timeframeToDateFns[timeFrame];
@ -81,18 +86,19 @@ export class ReportsService {
// Optimize performance for ALL_TIME by skipping eachOfInterval for time // Optimize performance for ALL_TIME by skipping eachOfInterval for time
// between 1970 and first listen // between 1970 and first listen
if (timePreset.timePreset === TimePreset.ALL_TIME) { if (timePreset.timePreset === TimePreset.ALL_TIME) {
let firstListen = min(listens.map(({ playedAt }) => playedAt)); interval.start = startOfDay(results[0].date);
interval.start = startOfDay(firstListen);
} }
// TODO: This code blocks the eventloop for multiple seconds if running for
// a large interval and with many listens. Refactor to make this more
// efficient or make pauses for event loop.
const reportItems = eachOfInterval(interval).map((date) => { const reportItems = eachOfInterval(interval).map((date) => {
const count = listens.filter((listen) => const dayResults = results.find((listen) => isSame(date, listen.date));
isSame(date, listen.playedAt),
).length; return {
return { date: formatISO(date), count }; date: formatISO(date),
count:
dayResults && dayResults.listenCount
? Number.parseInt(dayResults.listenCount)
: 0,
};
}); });
return { items: reportItems }; return { items: reportItems };