#incrementing id

4 messages · Page 1 of 1 (latest)

ancient yacht
#

how can i have a field in my convex docvument that is incremtnally increasing like 1 2 3 for every document inserted it is increase by one= without having race condition

lyric ventureBOT
#

Hi @ancient yacht. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

You’re running into a classic “global counter” problem, which is exactly where Convex’s optimistic concurrency control (OCC) and components come in.

If you try to keep a monotonically increasing field by:

// PSEUDO – not recommended at scale
const last = await ctx.db.query("items").order("desc").first();
await ctx.db.insert("items", { seq: last.seq + 1, ... });

you’ll eventually get OCC conflicts or wrong values when many inserts happen in parallel, because multiple mutations read the same “last” value and then try to write conflicting updates. Convex will retry a few times and then throw a “write conflict” error in heavy contention scenarios. [Errors; OCC conflicts]

There are two supported patterns in the docs to avoid race conditions here:


1. Use a sharded counter component (recommended)

The Sharded Counter component is designed for high‑throughput, contention‑free counters. It spreads increments across multiple documents (“shards”) so parallel writes don’t conflict, but you can still read a single total. [Sharded counter; Sharding details]

Setup (once):

// convex/convex.config.ts
import { defineApp } from "convex/server";
import shardedCounter from "@convex-dev/sharded-counter/convex.config.js";

const app = defineApp();
app.use(shardedCounter);
export default app;
// convex/counter.ts
import { components } from "./_generated/api";
import { ShardedCounter } from "@convex-dev/sharded-counter";

export const seqCounter = new ShardedCounter(components.shardedCounter);

Then in your mutation that inserts documents:

#
import { mutation } from "./_generated/server";
import { seqCounter } from "./counter";

export const insertItem = mutation({
  args: { /* your args */ },
  handler: async (ctx, args) => {
    // Use a dedicated key, e.g. "items-seq"
    const keyCounter = seqCounter.for("items-seq");

    // Atomically increment the counter and then read it
    await keyCounter.inc(ctx);
    const seq = await keyCounter.count(ctx);

    await ctx.db.insert("items", {
      seq,
      // ...other fields from args
    });
  },
});

Because the component’s operations are atomic and sharded, concurrent calls to insertItem will each see a unique seq value without race conditions, and you avoid the “too many writes to the same document” OCC problem. [Sharded counter; Counting docs]


2. Be aware of single‑document counters (only for low write rates)

You can keep a count in a single document and increment it in a trigger or helper, but the docs explicitly warn this increases write contention and can cause OCC conflicts if updated frequently. [Denormalizing count; Scaling tutorial]

So for “every insert gets the next integer” and especially under concurrency, the documented, scalable solution is to use the Sharded Counter component and derive your incremental field from it.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: