-- #5296: keep ListenBrainz's whole similar-recordings answer, and record every -- fetch, so the library match is made locally and can be made again. -- -- Until now the worker kept only the recordings already in the library (at most -- 20 of LB's 50) and threw the rest away. Two things followed: -- -- * A recording that reached the library later — a Lidarr import, or an MBID -- filled in by the AcoustID lookup — was not linked until its seed was -- fetched again. -- * A seed with no in-library match wrote nothing at all, and freshness was -- read from track_similarity, so that seed was never fresh. With the queue -- ordered by id, 25 such seeds held its head and were re-asked every hour -- while 2,449 others waited (#3879). -- -- The listenbrainz rows in track_similarity are now derived from this cache by -- ResolveListenBrainzTrackEdges. CREATE TABLE listenbrainz_similar_recordings ( seed_track_id uuid NOT NULL REFERENCES tracks(id) ON DELETE CASCADE, recording_mbid text NOT NULL, score DOUBLE PRECISION NOT NULL, PRIMARY KEY (seed_track_id, recording_mbid) ); -- The resolve joins the cache to tracks by recording MBID. CREATE INDEX listenbrainz_similar_recordings_mbid_idx ON listenbrainz_similar_recordings (recording_mbid); -- One row per seed that ListenBrainz has answered for, an empty answer -- included. The worker's queue reads this, not the edges. CREATE TABLE track_similarity_fetches ( track_id uuid PRIMARY KEY REFERENCES tracks(id) ON DELETE CASCADE, fetched_at timestamptz NOT NULL DEFAULT now(), returned integer NOT NULL ); -- The same for artists. Their answer is already kept, in artist_similarity and -- artist_similarity_unmatched; this only stops an empty answer being re-asked. CREATE TABLE artist_similarity_fetches ( artist_id uuid PRIMARY KEY REFERENCES artists(id) ON DELETE CASCADE, fetched_at timestamptz NOT NULL DEFAULT now(), returned integer NOT NULL );