브라우저 확장/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주일 꼬박 테스트 시스템 구현을 한 꼴이다. 토끼굴이 너무 깊다…