Does blockConcurrencyWhile() block the entire Durable Object?

I have a DO with two methods:
export class MyDO extends DurableObject {
async slowWrite() {
return this.ctx.blockConcurrencyWhile(async () => {
await fetch('https://slow-api.com'); // 15 seconds
await this.ctx.storage.put('key', 'value');
});
}

async fastRead() {
return await this.ctx.storage.get('key');
}
}
export class MyDO extends DurableObject {
async slowWrite() {
return this.ctx.blockConcurrencyWhile(async () => {
await fetch('https://slow-api.com'); // 15 seconds
await this.ctx.storage.put('key', 'value');
});
}

async fastRead() {
return await this.ctx.storage.get('key');
}
}
Problem: When User A calls slowWrite() via RPC, User B's fastRead() waits 15 seconds too. Questions: 1. Does blockConcurrencyWhile() put the entire DO in a "locked" state, blocking ALL incoming RPC calls? 2. Proposed solution - Route through fetch() instead:
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);

if (request.method === 'POST' && url.pathname === '/write') {
// WITH blocking
return this.ctx.blockConcurrencyWhile(async () => {
return this.slowWrite();
});
}

if (request.method === 'GET' && url.pathname === '/read') {
// WITHOUT blocking
return this.fastRead();
}
}
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);

if (request.method === 'POST' && url.pathname === '/write') {
// WITH blocking
return this.ctx.blockConcurrencyWhile(async () => {
return this.slowWrite();
});
}

if (request.method === 'GET' && url.pathname === '/read') {
// WITHOUT blocking
return this.fastRead();
}
}
Will this allow GET /read to execute without waiting when POST /write is running? Thanks! 🙏
1 Reply
Hetso
HetsoOP3d ago
Got it, thanks for clarifying! Follow-up question: is there a way to block concurrency for a specific DO method rather than the entire object? Just to clarify - when you said "use traditional locks", did you mean implementing it manually like this with a Promise queue?
export class MyDO extends DurableObject {
private writeQueue: Promise<any> = Promise.resolve();

async slowWrite() {
// Save reference to the current queue
const previousTask = this.writeQueue;

// Create a new promise and save its resolver
let resolveThis: () => void;
this.writeQueue = new Promise(resolve => {
resolveThis = resolve;
});

// Wait for the previous task to complete
await previousTask;

try {
await fetch('https://slow-api.com'); // 15 seconds
await this.ctx.storage.put('key', 'value');
} finally {
resolveThis!(); // Release the lock
}
}

async fastRead() {
// Executes without blocking
return await this.ctx.storage.get('key');
}
}
export class MyDO extends DurableObject {
private writeQueue: Promise<any> = Promise.resolve();

async slowWrite() {
// Save reference to the current queue
const previousTask = this.writeQueue;

// Create a new promise and save its resolver
let resolveThis: () => void;
this.writeQueue = new Promise(resolve => {
resolveThis = resolve;
});

// Wait for the previous task to complete
await previousTask;

try {
await fetch('https://slow-api.com'); // 15 seconds
await this.ctx.storage.put('key', 'value');
} finally {
resolveThis!(); // Release the lock
}
}

async fastRead() {
// Executes without blocking
return await this.ctx.storage.get('key');
}
}
Thanks

Did you find this page helpful?