Web Dev Cody
Web Dev Cody•6mo ago

Need help understanding the mutations and OCC

I'm working on a million checkboxes type of clone, and I have a function called toggleCheckbox which will first query a record, toggle a bit on or off in the number, and then write that number. This all seems to work fine. I'm also using another metadata table to keep a running sum of how many checkboxes are on or off. It seems like the counts are getting off for some reason. I created a cron which randomly toggles a bit using that same method described above every second in the first set of checkboxes. That also seems to work fine. The issue is when I manually start clicking checkboxes while the cron is running that my count seems to get off. I think there is something I'm not understanding about the concurrency model.
3 Replies
Web Dev Cody
Web Dev CodyOP•6mo ago
here is some code for context
// checkboxes.ts
export const updateCheckbox = mutation({
args: {
partition: v.number(),
index: v.number(),
},
async handler(ctx, args) {
const partition = await ctx.db
.query("checkboxes")
.withIndex("by_partition", (q) => q.eq("partition", args.partition))
.first();

if (!partition) {
await ctx.db.insert("checkboxes", {
partition: args.partition,
value: 1 << args.index,
});
await ctx.scheduler.runAfter(0, internal.metadata.updateRunningSum, {
isToggledOn: true,
});
} else {
const bit = 1 << args.index;
partition.value = bit ^ partition.value;
await ctx.db.patch(partition._id, partition);
await ctx.scheduler.runAfter(0, internal.metadata.updateRunningSum, {
isToggledOn: (partition.value & bit) > 0,
});
}
},
});

export const randomlyCheckABox = internalMutation({
async handler(ctx) {
const index = Math.floor(Math.random() * 32);
await ctx.scheduler.runAfter(0, api.checkboxes.updateCheckbox, {
partition: 0,
index,
});
},
});
// checkboxes.ts
export const updateCheckbox = mutation({
args: {
partition: v.number(),
index: v.number(),
},
async handler(ctx, args) {
const partition = await ctx.db
.query("checkboxes")
.withIndex("by_partition", (q) => q.eq("partition", args.partition))
.first();

if (!partition) {
await ctx.db.insert("checkboxes", {
partition: args.partition,
value: 1 << args.index,
});
await ctx.scheduler.runAfter(0, internal.metadata.updateRunningSum, {
isToggledOn: true,
});
} else {
const bit = 1 << args.index;
partition.value = bit ^ partition.value;
await ctx.db.patch(partition._id, partition);
await ctx.scheduler.runAfter(0, internal.metadata.updateRunningSum, {
isToggledOn: (partition.value & bit) > 0,
});
}
},
});

export const randomlyCheckABox = internalMutation({
async handler(ctx) {
const index = Math.floor(Math.random() * 32);
await ctx.scheduler.runAfter(0, api.checkboxes.updateCheckbox, {
partition: 0,
index,
});
},
});
// metadata.ts

export const updateRunningSum = internalMutation({
args: {
isToggledOn: v.boolean(),
},
async handler(ctx, args) {
const metadata = (await ctx.db.query("metadata").first())!;
metadata.totalChecked += args.isToggledOn ? 1 : -1;
await ctx.db.patch(metadata._id, {
totalChecked: metadata.totalChecked,
});
},
});

export const getTotalChecked = query({
async handler(ctx) {
const metadata = await ctx.db.query("metadata").first();
return metadata ? metadata.totalChecked : 0;
},
});
// metadata.ts

export const updateRunningSum = internalMutation({
args: {
isToggledOn: v.boolean(),
},
async handler(ctx, args) {
const metadata = (await ctx.db.query("metadata").first())!;
metadata.totalChecked += args.isToggledOn ? 1 : -1;
await ctx.db.patch(metadata._id, {
totalChecked: metadata.totalChecked,
});
},
});

export const getTotalChecked = query({
async handler(ctx) {
const metadata = await ctx.db.query("metadata").first();
return metadata ? metadata.totalChecked : 0;
},
});
all the UI does is call updateCheckbox({ index: index % BITS, partition: partitionIndex, }); when a checkbox is clicked, so the mutation should be able to figure out the final state of the box just found the issue 😆 const index = Math.floor(Math.random() * 31); // need to use 31 not 32
Web Dev Cody
Web Dev CodyOP•6mo ago
yeah, am using that as a reference a bit but doing my own approach

Did you find this page helpful?