sujayakar
sujayakar
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
I know some libraries have a dev mode where they freeze objects (so it'll throw an error if you try to mutate them), but there's a performance penalty to doing so
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
amazing! sorry you ran into this, it's definitely a gotcha with these types of APIs in JS
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
makes it less annoying (both for convex optimistic updates but also react, redux, etc.)
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
btw, one library I really like for working with this type of stuff is https://immerjs.github.io/immer/ -- it makes it look like you're just updating stuff directly but creates new objects under the hood as needed
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
so it's creating a new JS object rather than updating the original taskStatus object that lives within the remote query set
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
in this case, it should work if you change updateTaskCompletion to
const newStatus = { ...taskStatus, taskState: TaskState.COMPLETING, checkStartTime: Date.now };
this.updateTaskStatus(newStatus);
const newStatus = { ...taskStatus, taskState: TaskState.COMPLETING, checkStartTime: Date.now };
this.updateTaskStatus(newStatus);
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
ah since currentValue is a number it doesn't get mutated (currentValue + increment creates a new primitive)
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
instead, you can make a shallow copy of the object and then pass that to updateTaskStatus
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
ah, yeah it looks like updateTaskCompletion is mutating taskStatus, which originates from localStore
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
sure!
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
one potential issue is that optimistic updates must not mutate the query values they observe (like setState in react) -- that might be causing this anomaly.
22 replies
CCConvex Community
Created by Starlord on 12/13/2024 in #support-community
mutation doesnt rollback query state on failure
hi @Starlord ! do you have a small repro of the behavior you're observing? I'd be curious what your optimistic update looks like, in particular.
22 replies
CCConvex Community
Created by cyremur on 9/30/2024 in #support-community
Delta updates on query? - understanding bandwidth
hmm, not that I'm aware of. one idea for a workaround would be to switch to using zod validators. it'd be less nicely integrated with everything but still pretty close.
25 replies
CCConvex Community
Created by David Alonso on 12/11/2024 in #support-community
Debounce calls to a mutation without doing so for the optimistic update
the main trick is to be able to know when a mutation goes from (2) to (3). we do that by (ab)using regular optimistic updates. the data fetching query (https://github.com/sujayakar/tldraw-convex/blob/main/convex/getRoom.ts) returns its usual stuff + a null mutation ID, and then the optimistic update for (2) sets that field in the local store. let me know if that code makes sense + i'm happy to code review anything on your side! we haven't packaged this up into something reusable yet, but it's on our radar for v2 of our optimistic updates API.
10 replies
CCConvex Community
Created by David Alonso on 12/11/2024 in #support-community
Debounce calls to a mutation without doing so for the optimistic update
here's the state manager: https://github.com/sujayakar/tldraw-convex/blob/main/src/multiplayer/ConvexRoomManager.ts the core idea is to track an "operation" through a few phases: 1. it's submitted by the user and should show up immediately in the UI 2. it gets batched up into a mutation and sent to the server 3. it gets applied on the server and reflected in all queries.
10 replies
CCConvex Community
Created by David Alonso on 12/11/2024 in #support-community
Debounce calls to a mutation without doing so for the optimistic update
hey @David Alonso, @ian's been working on collab text editing on convex (https://github.com/get-convex/prosemirror-sync). if that'd be a drop in for the functionality you want, the two of you should chat. the text editing stuff is a bit early but will work. if you'd like to debounce mutations yourself (rather than using a text editor component), I have a pattern from https://github.com/sujayakar/tldraw-convex that uses single flighted mutations and has immediate optimistic updates. lmk and I can go into more detail for how it works.
10 replies
CCConvex Community
Created by cyremur on 9/30/2024 in #support-community
Delta updates on query? - understanding bandwidth
was trying it with 10 repetitions of a 40KB json document (not a representative test for compression ratio, ofc)
encode: 0ms
compress: 14ms
decompress: 8ms
[lz4] Compressed 407.22KB to 15.33KB (ratio: 0.04)
encode: 0ms
compress: 14ms
decompress: 8ms
[lz4] Compressed 407.22KB to 15.33KB (ratio: 0.04)
25 replies
CCConvex Community
Created by cyremur on 9/30/2024 in #support-community
Delta updates on query? - understanding bandwidth
makes sense. yeah, I think splitting stuff up into smaller documents will make a lot of stuff better automatically -- mutations will be cheaper when they only fetch what they need, and queries can be finer-grained, have fewer reactivity updates, get cached more effectively. but, understood how this then means pushing database access into your game logic. on the other extreme, if you're storing everything in one big game state document, have you tried compressing the game state before writing it to the db? this is really quick & dirty, and it'll make the dashboard not that useful, but it could be worth trying. i've used lz4js in queries/mutations and it works great:
import { v } from "convex/values";
import { mutation } from "./_generated/server"
import * as lz4 from "lz4js";

export const compressionTest = mutation({
args: {
repetitions: v.number()
},
handler: async (ctx, args) => {
let s = [];
for (let i = 0; i < args.repetitions; i++) {
s.push(example);
}
console.time("encode");
const encoder = new TextEncoder();
const buf = encoder.encode(s.join("\n"));
console.timeEnd("encode");

console.time("compress");
const compressed = lz4.compress(buf);
console.timeEnd("compress");

console.time("decompress");
const decompressed = lz4.decompress(compressed);
console.timeEnd("decompress");

if (!decompressed.every((value, index) => value === buf[index])) {
throw new Error("Decompressed data does not match original data");
}

console.log(`[lz4] Compressed ${(buf.length / 1024).toFixed(2)}KB to ${(compressed.length / 1024).toFixed(2)}KB (ratio: ${(compressed.length / buf.length).toFixed(2)})`);
}
})
import { v } from "convex/values";
import { mutation } from "./_generated/server"
import * as lz4 from "lz4js";

export const compressionTest = mutation({
args: {
repetitions: v.number()
},
handler: async (ctx, args) => {
let s = [];
for (let i = 0; i < args.repetitions; i++) {
s.push(example);
}
console.time("encode");
const encoder = new TextEncoder();
const buf = encoder.encode(s.join("\n"));
console.timeEnd("encode");

console.time("compress");
const compressed = lz4.compress(buf);
console.timeEnd("compress");

console.time("decompress");
const decompressed = lz4.decompress(compressed);
console.timeEnd("decompress");

if (!decompressed.every((value, index) => value === buf[index])) {
throw new Error("Decompressed data does not match original data");
}

console.log(`[lz4] Compressed ${(buf.length / 1024).toFixed(2)}KB to ${(compressed.length / 1024).toFixed(2)}KB (ratio: ${(compressed.length / buf.length).toFixed(2)})`);
}
})
25 replies
CCConvex Community
Created by cyremur on 9/30/2024 in #support-community
Delta updates on query? - understanding bandwidth
yeah, i’d be curious if we could find a way to make the bandwidth close to sizeof(action) * 100 actions. on one extreme convex could just sync an action log, but there should be ways to tweak the server data model to get close to this without fully upending everything.
25 replies
CCConvex Community
Created by cyremur on 9/30/2024 in #support-community
Delta updates on query? - understanding bandwidth
also curious how the tables and queries are set up for the game — maybe there are some easy wins like the document splitting idea from before
25 replies