syncguard
Version:
Functional TypeScript library for distributed locking across microservices. Prevents race conditions with Redis, Firestore, and custom backends. Features automatic lock management, timeout handling, and extensible architecture.
44 lines (43 loc) • 1.91 kB
JavaScript
/* SPDX-FileCopyrightText: 2025-present Kriasoft */
/* SPDX-License-Identifier: MIT */
import { withRetries } from "../retry.js";
/**
* Creates a release operation for Firestore backend
*/
export function createReleaseOperation(db, locksCollection, config) {
return async (lockId) => {
return withRetries(async () => {
// First, find the document by querying for lockId to get the key
const querySnapshot = await locksCollection
.where("lockId", "==", lockId)
.limit(1)
.get();
if (querySnapshot.empty) {
return false;
}
const doc = querySnapshot.docs[0];
const data = doc.data();
// Use transaction for atomic ownership verification and deletion
const result = await db.runTransaction(async (trx) => {
// Use the exact document reference from the query to avoid TOCTOU issues
const docRef = doc.ref;
const currentDoc = await trx.get(docRef);
// Handle race condition: document might have been deleted between query and transaction
if (!currentDoc.exists) {
// Document was deleted between query and transaction - treat as success
// since our lockId was found initially but document no longer exists
return true;
}
const currentData = currentDoc.data();
// Verify ownership by lockId - this is critical for safety
if (currentData.lockId !== lockId) {
// Document exists but lockId changed - another lock took this key
return false;
}
trx.delete(docRef);
return true;
});
return result;
}, config);
};
}