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

Move delay before broadcasting to clients

Replace per-socket 5s wait with a single 4s pause before iterating
clients to avoid per-client staggering and reduce total delay Move delay
before broadcasting to clients

Wait 4s before fetching broadcast payload and reuse the results for all
matching clients instead of delaying per-socket. This avoids redundant
data retrieval and reduces overall broadcast latency.

Wrap Effect pipelines in getScrobbles, getScrobblesChart and
getActorScrobbles with Effect.runPromise so they execute and return
promises.

+57 -50
+13 -12
apps/ws/src/main.ts
··· 68 68 69 69 logger.info` Cursor: ${event.time_us}`; 70 70 71 - for (const [socket, channels] of clients) { 72 - if (channels.has(collection) && socket.readyState === WebSocket.OPEN) { 73 - try { 74 - await new Promise((resolve) => setTimeout(resolve, 5000)); 75 - const nowPlayings = await getNowPlayings(ctx); 76 - const scrobbles = await getScrobbles(ctx); 77 - const scrobblesChart = await getScrobblesChart(ctx); 78 - const actorScrobbles = await getActorScrobbles(ctx, event.did); 79 - const actorAlbums = await getActorAlbums(ctx, event.did); 80 - const actorArtists = await getActorArtists(ctx, event.did); 71 + await new Promise((resolve) => setTimeout(resolve, 4000)); 72 + 73 + try { 74 + const nowPlayings = await getNowPlayings(ctx); 75 + const scrobbles = await getScrobbles(ctx); 76 + const scrobblesChart = await getScrobblesChart(ctx); 77 + const actorScrobbles = await getActorScrobbles(ctx, event.did); 78 + const actorAlbums = await getActorAlbums(ctx, event.did); 79 + const actorArtists = await getActorArtists(ctx, event.did); 81 80 81 + for (const [socket, channels] of clients) { 82 + if (channels.has(collection) && socket.readyState === WebSocket.OPEN) { 82 83 socket.send( 83 84 JSON.stringify({ 84 85 nowPlayings, ··· 91 92 did: event.did, 92 93 }), 93 94 ); 94 - } catch (error) { 95 - logger.error`Failed to send data to client: ${error}`; 96 95 } 97 96 } 97 + } catch (error) { 98 + logger.error`Failed to send data to client: ${error}`; 98 99 } 99 100 } 100 101 });
+16 -14
apps/ws/src/services/getActorScrobbles.ts
··· 3 3 import { deepCamelCaseKeys } from "../lib/deepCamelKeys.ts"; 4 4 5 5 export default function (ctx: Context, did: string) { 6 - return pipe( 7 - retrieve({ 8 - ctx, 9 - params: { 10 - did, 11 - offset: 0, 12 - limit: 10, 13 - }, 14 - }), 15 - Effect.flatMap(presentation), 16 - Effect.retry({ times: 3 }), 17 - Effect.timeout("10 seconds"), 18 - Effect.catchAll((error) => 19 - Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 6 + return Effect.runPromise( 7 + pipe( 8 + retrieve({ 9 + ctx, 10 + params: { 11 + did, 12 + offset: 0, 13 + limit: 10, 14 + }, 15 + }), 16 + Effect.flatMap(presentation), 17 + Effect.retry({ times: 3 }), 18 + Effect.timeout("10 seconds"), 19 + Effect.catchAll((error) => 20 + Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 21 + ), 20 22 ), 21 23 ); 22 24 }
+14 -12
apps/ws/src/services/getScrobbles.ts
··· 4 4 import { eq, desc } from "drizzle-orm"; 5 5 6 6 export default function (ctx: Context, offset?: number, limit?: number) { 7 - return pipe( 8 - retrieve({ 9 - ctx, 10 - params: { 11 - offset: offset || 0, 12 - limit: limit || 20, 13 - }, 14 - }), 15 - Effect.retry({ times: 3 }), 16 - Effect.timeout("10 seconds"), 17 - Effect.catchAll((error) => 18 - Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 7 + return Effect.runPromise( 8 + pipe( 9 + retrieve({ 10 + ctx, 11 + params: { 12 + offset: offset || 0, 13 + limit: limit || 20, 14 + }, 15 + }), 16 + Effect.retry({ times: 3 }), 17 + Effect.timeout("10 seconds"), 18 + Effect.catchAll((error) => 19 + Effect.fail(new Error(`Failed to retrieve scrobbles: ${error}`)), 20 + ), 19 21 ), 20 22 ); 21 23 }
+14 -12
apps/ws/src/services/getScrobblesChart.ts
··· 4 4 import tables from "../schema/mod.ts"; 5 5 6 6 export default function (ctx: Context, did?: string) { 7 - return pipe( 8 - retrieve({ 9 - ctx, 10 - params: { 11 - did, 12 - }, 13 - }), 14 - Effect.flatMap(presentation), 15 - Effect.retry({ times: 3 }), 16 - Effect.timeout("10 seconds"), 17 - Effect.catchAll((error) => 18 - Effect.fail(new Error(`Failed to retrieve scrobbles chart: ${error}`)), 7 + return Effect.runPromise( 8 + pipe( 9 + retrieve({ 10 + ctx, 11 + params: { 12 + did, 13 + }, 14 + }), 15 + Effect.flatMap(presentation), 16 + Effect.retry({ times: 3 }), 17 + Effect.timeout("10 seconds"), 18 + Effect.catchAll((error) => 19 + Effect.fail(new Error(`Failed to retrieve scrobbles chart: ${error}`)), 20 + ), 19 21 ), 20 22 ); 21 23 }