브라우저 확장/Promise를 이용한 동시성 문제 해결
개요
(이하 Copilot이 작성한 개요. 꽤 정확해서 놀랐다)
브라우저 확장 프로그램에서는 chrome.storage
를 이용하여 데이터를 저장할 수 있습니다. 하지만 chrome.storage
는
비동기적으로 동작하기 때문에, 동시성 문제가 발생할 수 있습니다. 이를 해결하기 위해 Promise
를 이용하여 동시성 문제를
해결해보겠습니다.
문제 및 해결책 제안
일전에 테스트 시스템을 열심히 만들었기 때문에 테스트 코드로 보여주는 것이 빠를 것 같다.
/// <reference types="jasmine" />
describe('storage', () => {
// This spec will fail if executed
xit('fails to atomic update if lock does not exist', async () => {
await new Promise<void>((resolve) => {
chrome.storage.local.set({ counter: '0' }, resolve);
});
let ps = [];
for (let i = 0; i < 10; i++) {
ps.push(new Promise<void>((res2) => {
setTimeout(() => {
chrome.storage.local.get('counter', ({ counter }) => {
chrome.storage.local.set({ counter: Number(counter) + 1 }, res2);
});
}, i);
}));
}
await Promise.all(ps);
const result = await new Promise((resolve) => {
chrome.storage.local.get(['counter'], (res) => {
resolve(res["counter"]);
});
});
expect(result).toBe(10); // FAIL: Usually it shows around 5
});
it('updates multiple times', async () => {
let counterLock = Promise.resolve();
await new Promise<void>((resolve) => {
chrome.storage.local.set({ counter: '0' }, resolve);
});
for (let i = 0; i < 10; i++) {
counterLock = counterLock.then(() => {
return new Promise((res2) => {
chrome.storage.local.get('counter', ({ counter }) => {
chrome.storage.local.set({ counter: Number(counter) + 1 }, res2);
});
});
});
// Unnecessary, just to showcase
if (i === 5) {
await counterLock;
}
}
await counterLock;
const result = await new Promise((resolve) => {
chrome.storage.local.get(['counter'], (res) => {
resolve(res["counter"]);
});
});
expect(result).toBe(10);
});
});
즉, 데이터베이스를 업데이트하는 작업을 counterLock
이라는 Promise에 연결해서, 해당 lock을 쓰는 한 각 작업들이 다른
작업과 동시에 실행되지 않도록 보장하는 것이다.
결론
그래서 결국 이거 쓰겠다고 1주일 꼬박 테스트 시스템 구현을 한 꼴이다. 토끼굴이 너무 깊다…