Looking up archived cards no longer reloads every prototype

When a demo is shared, Obeya goes over every shared page and looks up its card, hundreds of them on a long-used canvas. For each archived card it used to read all of the canvas's prototypes from the database again, convert them and fetch their ideas' titles one by one. Now it reads which cards are prototypes once per change of the canvas and converts each prototype only when a card first needs it.

One pass of sharing over all shared cards (server CPU time)

Test canvas: 900 cards (800 archived), 80 ideas with 320 prototypes, 460 shared pages

Before
1203 ms
After
20 ms

Dashed line: the half second the test allows. The test fails on the old code and passes on the new.

Copy of the real Obeya canvas: 732 archived cards, 261 shared pages, 8 prototypes

Before
75 ms
After
11 ms

Median of five passes on an idle machine. With other work running on the machine, the same pass took 200–400 ms before the change and stayed around 15 ms after it.

Many cards landing one after another (server CPU time per run of 30 landings)

Same test canvas

Before
500 ms
After
185 ms

Every change of the canvas used to read every prototype in full again. Now it reads only which cards are prototypes and reuses the cards already on the canvas.

Same result

identical The canvas, the archive and every card looked up on its own, built from a copy of the real database, are the same before and after the change. This was checked on the Obeya canvas and on a second canvas that has prototypes. A new test also checks that an archived idea and its prototype, each looked up on its own, match what the archive lists.