How I struggled to fix votes on Sanity
Ever since I implemented upvotes a few months ago, I had been struggling with user upvotes/downvotes request occasionly timing out. The bug persisted for a few months and the few times I tried to debug it, I had no success. Is it the database schema? Nope, I use similar schemas for other collections and they work fine. An inefficient MongoDB query? Same thing. No indexing? I indexed the DB even though there are barely any votes in the collection. An issue with Vercel cold start? Also not it, everything within the norm.
Last Friday the rest of the app was finally ready and I wanted to start inviting some users, so I gave up and decided to pay $20/month for Vercel Pro to increase the timeout from 10 to 60 seconds and worry about the bug another day. And then I checked the logs on Vercel Pro...
Unhandled error: MongooseError: Operation `userVotes.findOne()` buffering timed out after 10000ms
at Timeout.<anonymous> (/var/task/sanity_client/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js:175:23)
at listOnTimeout (node:internal/timers:569:17)
at process.processTimers (node:internal/timers:512:7)
Because Mongoose timeout is 10000ms and Vercel's timeout is also 10000ms but this includes the cold start time, this error never popped up on my free plan.
The error suggested the issue is with my MongoDB connection, which looks roughly like this:
import { ConnectionStates, connect } from "mongoose";
import { MONGODB_URI } from "@/utils";
if (!MONGODB_URI) {
throw new Error(
"Please define the MONGODB_URI environment variable inside .env.local",
);
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
// @ts-ignore
let cached = global.mongoose;
if (!cached) {
// @ts-ignore
cached = global.mongoose = { connection: null, promise: null };
}
export async function connectMongoose() {
if (
cached.connection &&
cached.connection.readyState === ConnectionStates.connected
) {
return cached.connection;
}
if (
!cached.promise ||
cached.connection?.readyState !== ConnectionStates.connecting
) {
cached.promise = connect(MONGODB_URI!, {});
cached.connection = await cached.promise;
}
return cached.connection;
}
A 5-minute debug session later, it turned out I forgot to await the connection. I await it in all other API endpoints but forgot to include it in this one π€¦ββοΈ I added the following line of code, deployed and the bug that persisted on Sanity for months was gone:
await connectMongoose();
Who knows, maybe there's someone out there also struggling with MongoDB timeouts on Vercel and they'll find this helpful.