创建Google Room Resource后插入ACL频繁报错的解决方案咨询
我有一个Google Cloud函数,先通过Google Admin SDK的resources.calendars.insert方法创建Google Room Resource,随后立即尝试通过Google Calendar API的Acl: insert方法插入ACL。相关代码如下:
const AdminService = google.admin({version: 'directory_v1'}); try { const response = await AdminService.resources.calendars.insert(options); // options omitted } catch (error) { console.log(`Google Room Resource FAIL`); console.error(error.message); } await new Promise((r) => setTimeout(r, 10000)); const CalendarService = google.calendar({version: 'v3'}); try { const res = await CalendarService.acl.insert(option); // options omitted console.log(res); } catch (error) { console.log(error); throw new Error(error.message); }
认证方面,我使用拥有正确权限范围的服务账号,模拟具备相应权限的管理员用户。生成所需JWT令牌的代码如下:
const generateJWT = async (scope:string[])=>{ const jwtClient = new google.auth.JWT( client_email, // service account undefined, private_key, scope, subject // admin user ); return jwtClient; }
在每个API调用的options参数中,我直接为auth属性获取令牌,示例如下:
const option = { 'calendarId': acl.calendarId, 'auth': await generateJWT('https://www.googleapis.com/auth/calendar'), 'resource': { 'role': acl.role, 'scope': { 'type': acl.scopeType, 'value': acl.scopeValue, }, }, };
由于我对所有API调用都使用了await,本以为收到响应时Google Workspace已完成数据同步,但未使用setTimeout时总会返回Error: Not Found。最初设置5秒超时可用,后来失效改为10秒,但现在仍偶尔出现该错误。我不认可setTimeout这种临时方案,且它并不可靠,请问如何在不搭建队列等额外基础设施的情况下处理这种异步同步问题?
1. 实现指数退避的重试逻辑
针对ACL插入操作,当捕获到Not Found错误时自动重试,重试间隔按指数递增(比如1s、2s、4s、8s),同时限制最大重试次数(建议3-5次)。这种方式能自适应Google内部数据同步的延迟波动,比固定超时更可靠。
修改后的ACL插入代码示例:
const CalendarService = google.calendar({version: 'v3'}); const maxRetries = 4; let retryCount = 0; let aclInsertSuccess = false; while (!aclInsertSuccess && retryCount < maxRetries) { try { const res = await CalendarService.acl.insert(option); console.log(res); aclInsertSuccess = true; } catch (error) { if (error.message.includes('Not Found') && retryCount < maxRetries) { retryCount++; const delay = Math.pow(2, retryCount) * 1000; // 指数级延迟 console.log(`日历未找到,${delay/1000}秒后重试...`); await new Promise(r => setTimeout(r, delay)); } else { console.log(error); throw new Error(error.message); } } }
2. 复用JWT客户端,减少冗余操作
当前每次API调用都重新生成JWT客户端,既冗余又可能增加不必要的延迟。可以提前生成包含所有所需权限的JWT客户端,供两个API调用共用:
// 提前生成并授权JWT客户端 const jwtClient = new google.auth.JWT( client_email, undefined, private_key, [ 'https://www.googleapis.com/auth/admin.directory.resource.calendar', 'https://www.googleapis.com/auth/calendar' ], subject ); await jwtClient.authorize(); // 创建会议室资源时复用 const adminOptions = { // ...其他参数 auth: jwtClient }; const response = await AdminService.resources.calendars.insert(adminOptions); // 插入ACL时同样复用 const aclOption = { 'calendarId': acl.calendarId, 'auth': jwtClient, 'resource': { 'role': acl.role, 'scope': { 'type': acl.scopeType, 'value': acl.scopeValue, }, }, };
3. 先验证日历存在性再执行ACL插入
在重试循环中,每次执行ACL插入前,先调用Calendar API的calendars.get接口确认目标日历已存在,避免无意义的等待:
// 在重试逻辑中添加日历验证步骤 try { // 先检查日历是否存在 await CalendarService.calendars.get({ calendarId: acl.calendarId, auth: jwtClient }); // 确认存在后插入ACL const res = await CalendarService.acl.insert(aclOption); console.log(res); aclInsertSuccess = true; } catch (error) { // 处理错误和重试逻辑 }
内容的提问来源于stack exchange,提问作者zlZimon

