A decentralized music tracking and discovery platform built on AT Protocol 🎵

Remove cache, use inArray, and add logging

Remove Effect Cache usage for getTopArtists/getTopTracks and call
retrieve directly. Replace SQL ANY(...) expressions with drizzle-orm
inArray for artist/track filters. Add consola.info statements to log
counts at key steps and remove now-unused imports (Cache, Duration, eq).

+32 -38
+16 -19
apps/api/src/xrpc/app/rocksky/charts/getTopArtists.ts
··· 1 1 import type { Context } from "context"; 2 2 import { consola } from "consola"; 3 - import { count, desc, sql, and, gte, lte } from "drizzle-orm"; 4 - import { Effect, pipe, Cache, Duration } from "effect"; 3 + import { count, desc, sql, and, gte, lte, inArray } from "drizzle-orm"; 4 + import { Effect, pipe } from "effect"; 5 5 import type { Server } from "lexicon"; 6 6 import type { ArtistViewBasic } from "lexicon/types/app/rocksky/artist/defs"; 7 7 import type { QueryParams } from "lexicon/types/app/rocksky/charts/getTopArtists"; ··· 9 9 import tables from "schema"; 10 10 11 11 export default function (server: Server, ctx: Context) { 12 - const getTopArtistsCache = Cache.make({ 13 - capacity: 100, 14 - timeToLive: Duration.minutes(5), 15 - lookup: (params: QueryParams) => 16 - pipe( 17 - { params, ctx }, 18 - retrieve, 19 - Effect.flatMap(presentation), 20 - Effect.retry({ times: 3 }), 21 - Effect.timeout("120 seconds"), 22 - ), 23 - }); 24 - 25 12 const getTopArtists = (params: QueryParams) => 26 13 pipe( 27 - getTopArtistsCache, 28 - Effect.flatMap((cache) => cache.get(params)), 14 + { params, ctx }, 15 + retrieve, 16 + Effect.flatMap(presentation), 17 + Effect.retry({ times: 3 }), 18 + Effect.timeout("120 seconds"), 29 19 Effect.catchAll((err) => { 30 20 consola.error(err); 31 21 return Effect.succeed({ artists: [] }); ··· 80 70 .offset(offset); 81 71 82 72 const topArtistsData = await topArtistsQuery.execute(); 73 + consola.info(`Found ${topArtistsData.length} top artists`); 83 74 84 75 if (topArtistsData.length === 0) { 85 76 return { data: [] }; ··· 88 79 const artistIds = topArtistsData 89 80 .map((a) => a.artistId) 90 81 .filter((id): id is string => id !== null); 82 + consola.info(`Extracted ${artistIds.length} artist IDs`); 91 83 92 84 const artists = await ctx.db 93 85 .select({ ··· 99 91 genres: tables.artists.genres, 100 92 }) 101 93 .from(tables.artists) 102 - .where(sql`${tables.artists.id} = ANY(${artistIds})`) 94 + .where(inArray(tables.artists.id, artistIds)) 103 95 .execute(); 96 + consola.info(`Retrieved ${artists.length} artist details`); 104 97 105 98 const artistMap = new Map(artists.map((artist) => [artist.id, artist])); 106 99 ··· 115 108 .from(tables.scrobbles) 116 109 .where( 117 110 and( 118 - sql`${tables.scrobbles.artistId} = ANY(${artistIds})`, 111 + inArray(tables.scrobbles.artistId, artistIds), 119 112 dateConditions.length > 0 ? and(...dateConditions) : undefined, 120 113 ), 121 114 ) 122 115 .groupBy(tables.scrobbles.artistId) 123 116 .execute(); 117 + consola.info( 118 + `Calculated unique listeners for ${uniqueListenersQuery.length} artists`, 119 + ); 124 120 125 121 const listenersMap = new Map( 126 122 uniqueListenersQuery.map((item) => [ ··· 146 142 }; 147 143 }) 148 144 .filter((item): item is TopArtist => item !== null); 145 + consola.info(`Returning ${result.length} top artists with complete data`); 149 146 150 147 return { data: result }; 151 148 },
+16 -19
apps/api/src/xrpc/app/rocksky/charts/getTopTracks.ts
··· 1 1 import type { Context } from "context"; 2 2 import { consola } from "consola"; 3 - import { count, desc, eq, sql, and, gte, lte } from "drizzle-orm"; 4 - import { Effect, pipe, Cache, Duration } from "effect"; 3 + import { count, desc, sql, and, gte, lte, inArray } from "drizzle-orm"; 4 + import { Effect, pipe } from "effect"; 5 5 import type { Server } from "lexicon"; 6 6 import type { SongViewBasic } from "lexicon/types/app/rocksky/song/defs"; 7 7 import type { QueryParams } from "lexicon/types/app/rocksky/charts/getTopTracks"; ··· 9 9 import tables from "schema"; 10 10 11 11 export default function (server: Server, ctx: Context) { 12 - const getTopTracksCache = Cache.make({ 13 - capacity: 100, 14 - timeToLive: Duration.minutes(5), 15 - lookup: (params: QueryParams) => 16 - pipe( 17 - { params, ctx }, 18 - retrieve, 19 - Effect.flatMap(presentation), 20 - Effect.retry({ times: 3 }), 21 - Effect.timeout("120 seconds"), 22 - ), 23 - }); 24 - 25 12 const getTopTracks = (params: QueryParams) => 26 13 pipe( 27 - getTopTracksCache, 28 - Effect.flatMap((cache) => cache.get(params)), 14 + { params, ctx }, 15 + retrieve, 16 + Effect.flatMap(presentation), 17 + Effect.retry({ times: 3 }), 18 + Effect.timeout("120 seconds"), 29 19 Effect.catchAll((err) => { 30 20 consola.error(err); 31 21 return Effect.succeed({ tracks: [] }); ··· 80 70 .offset(offset); 81 71 82 72 const topTracksData = await topTracksQuery.execute(); 73 + consola.info(`Found ${topTracksData.length} top tracks`); 83 74 84 75 if (topTracksData.length === 0) { 85 76 return { data: [] }; ··· 88 79 const trackIds = topTracksData 89 80 .map((t) => t.trackId) 90 81 .filter((id): id is string => id !== null); 82 + consola.info(`Extracted ${trackIds.length} track IDs`); 91 83 92 84 const tracks = await ctx.db 93 85 .select({ ··· 108 100 createdAt: tables.tracks.createdAt, 109 101 }) 110 102 .from(tables.tracks) 111 - .where(sql`${tables.tracks.id} = ANY(${trackIds})`) 103 + .where(inArray(tables.tracks.id, trackIds)) 112 104 .execute(); 105 + consola.info(`Retrieved ${tracks.length} track details`); 113 106 114 107 const trackMap = new Map(tracks.map((track) => [track.id, track])); 115 108 ··· 124 117 .from(tables.scrobbles) 125 118 .where( 126 119 and( 127 - sql`${tables.scrobbles.trackId} = ANY(${trackIds})`, 120 + inArray(tables.scrobbles.trackId, trackIds), 128 121 dateConditions.length > 0 ? and(...dateConditions) : undefined, 129 122 ), 130 123 ) 131 124 .groupBy(tables.scrobbles.trackId) 132 125 .execute(); 126 + consola.info( 127 + `Calculated unique listeners for ${uniqueListenersQuery.length} tracks`, 128 + ); 133 129 134 130 const listenersMap = new Map( 135 131 uniqueListenersQuery.map((item) => [ ··· 164 160 }; 165 161 }) 166 162 .filter((item): item is TopTrack => item !== null); 163 + consola.info(`Returning ${result.length} top tracks with complete data`); 167 164 168 165 return { data: result }; 169 166 },