Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/db/migrations/0013_podcasts_cadence.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- The podcasts sources actually poll every fifteen minutes.
--
-- #34 set `cadenceMinutes: 15` on the adapter, which decides what a source is
-- created with and nothing else. `insertSource` is idempotent on the slug and
-- its conflict clause updates `name`, `description` and `updated_at` -- not
-- `config`, and not `cadence_minutes`. That is deliberate: cadence is on the
-- list of things an operator may edit from the sources page, so re-asserting
-- the adapter's number on every boot would silently undo a human's decision the
-- next time the web app restarted.
--
-- The cost of that is this file. The two podcast sources were created while the
-- adapter still said 60, so they kept 60, and the change in #34 reached exactly
-- nothing that was already running. Nothing failed and no run errored; the
-- catalogue walk simply proceeded at a quarter of the intended rate, which is
-- the kind of wrong that is only visible if you go and look.
--
-- So the two rows are moved by hand, once. Scoped by slug rather than by
-- adapter so a source somebody added themselves against the same adapter keeps
-- whatever cadence they chose for it, and conditional on the current value
-- still being 60 so that re-running this never overwrites a later decision.
--
-- A fresh deployment reaches the same place without this: seeding creates both
-- sources from the adapter, which now says 15.

update sources
set cadence_minutes = 15,
updated_at = now()
where slug in ('podcasts-commercial', 'podcasts-self-hosted')
and cadence_minutes = 60;
53 changes: 53 additions & 0 deletions test/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -933,3 +933,56 @@ describe('the weather collection migration', () => {
expect(moved).toEqual({ sources: 1, items: 1, feeds: 1, left_behind: 0 });
});
});

describe('the podcasts cadence migration', () => {
test('moves the two seeded sources to fifteen minutes and nothing else', async () => {
// The gap 0013 closes: `insertSource` is idempotent on the slug but its
// conflict clause never touches cadence_minutes, so raising the adapter's
// cadence reached nothing that already existed.
const c = await one(
`insert into collections (slug, name) values ('podcasts', 'Podcasts')
on conflict (slug) do update set name = excluded.name returning id`,
);
for (const [slug, cadence] of [
['podcasts-commercial', 60],
['podcasts-self-hosted', 60],
// Somebody's own source against the same adapter, and one an operator has
// already tuned. Neither may be touched.
['podcasts-mine', 60],
['podcasts-self-hosted-tuned', 5],
]) {
await db.query(
`insert into sources (collection_id, adapter, slug, name, cadence_minutes)
values ($1, 'podcasts', $2, $2, $3)`,
[c.id, slug, cadence],
);
}
// Exactly the statement migration 0013 runs.
const stmt = `update sources set cadence_minutes = 15, updated_at = now()
where slug in ('podcasts-commercial', 'podcasts-self-hosted')
and cadence_minutes = 60`;
await db.query(stmt);

const after = await one(
`select
(select cadence_minutes from sources where slug = 'podcasts-commercial') as commercial,
(select cadence_minutes from sources where slug = 'podcasts-self-hosted') as self_hosted,
(select cadence_minutes from sources where slug = 'podcasts-mine') as mine,
(select cadence_minutes from sources where slug = 'podcasts-self-hosted-tuned') as tuned`,
);
expect(after).toEqual({ commercial: 15, self_hosted: 15, mine: 60, tuned: 5 });

// Re-running must not walk a later decision back to 15.
await db.query(`update sources set cadence_minutes = 60 where slug = 'podcasts-commercial'`);
await db.query(`update sources set cadence_minutes = 30 where slug = 'podcasts-self-hosted'`);
await db.query(stmt);
const again = await one(
`select
(select cadence_minutes from sources where slug = 'podcasts-commercial') as commercial,
(select cadence_minutes from sources where slug = 'podcasts-self-hosted') as self_hosted`,
);
// The first was genuinely back at 60, so it moves again; the second was
// deliberately set to 30 and is left alone.
expect(again).toEqual({ commercial: 15, self_hosted: 30 });
});
});
Loading