Skip to content

🐛 Bug: ⁠queryCollection stops re-rendering after rapid inserts with delayed ⁠onInsert #478

Description

@treyhoover

When adding artificial latency to ⁠onInsert and inserting two items in quick succession, the UI stops re-rendering further updates.

A ⁠setInterval log of the collection state confirms it is correct in memory (but not before the last render is logged, hinting at a race condition).
Removing ⁠orderBy in the live query fixes the render but throws a different error (see below).

export const todosCollection = createCollection(
  queryCollectionOptions({
    queryKey: ["todos"],
    queryFn: async () => {
      const todos = await supabase.from("todos").select("*");
      if (todos.error) throw todos.error;

      return todos.data;
    },
    queryClient,
    getKey: (item) => item.id,
    onInsert: async ({ transaction }): Promise<void> => {
      await sleep(1000);

      await Promise.all(
        transaction.mutations.map(async (item) => {
          const todo = await supabase.from("todos").insert(item.modified);
          if (todo.error) throw todo.error;
        }),
      );
    },

    onUpdate: async ({ transaction }): Promise<void> => {
      await Promise.all(
        transaction.mutations.map(async (item) => {
          const todo = await supabase.from("todos").update(item.modified).eq("id", item.modified.id);

          if (todo.error) throw todo.error;

          return todo.data;
        }),
      );
    },

    onDelete: async ({ transaction }): Promise<void> => {
      const ids = transaction.mutations.map((m) => m.key);
      const res = await supabase.from("todos").delete().in("id", ids);
      if (res.error) throw res.error;
    },
  }),
);
  const todosQuery = useLiveQuery(
    (q) => q.from({ todo: todosCollection }).orderBy(({ todo }) => todo.created_at, "asc"),
    [user.id],
  );

If I remove orderBy on the live query it renders correctly and surfaces this error:
Uncaught (in promise) TransactionError: You can no longer call .rollback() as the transaction is already completed