Knex로 테스트를 10배 더 빠르게 만든 방법

오늘 저는 가정을 하기보다 먼저 측정하는 것의 중요성을 상기했습니다.

나는 그것을 통해 걷는 게시물을 작성한다고 생각했습니다. 다른 사람에게 도움이된다면 적어도 유용한 것이 나올 수 있습니다.

배경



지난 며칠 동안 테스트 속도를 개선하기 위해 노력했습니다. 처음에는 괜찮았지만 더 많은 테스트를 추가할수록 실행 시간이 더 오래 걸렸습니다. 일부 섹션에서 각 테스트가 600-1300ms가 걸리는 지점에 도달했을 때 나는 그것에 대해 뭔가를 할 수 있을 만큼 지쳤습니다.



출발점



나는 그것이 아마도 데이터베이스 일 것이라고 생각했지만 도움이 될 스모킹 건이 있는지 알아보기 위해 먼저 일부basic Node profiling를 시도하는 것이 좋겠다고 생각했습니다.

해당 페이지의 지침에는 --prof가 노드 프로파일링을 활성화한다고 나와 있습니다. Node를 "그냥"실행하는 것이 아니라 Mocha를 실행하여 프로필 결과를 얻고 싶었습니다. 플래그를 --v8-prof로 추가하면 Mocha가 플래그를 노드로 전달합니다.

$ NODE_ENV=test mocha --v8-prof --require test/fixtures.js
$ node --prof-process isolate-0x102d57000-8614-v8.log > processed.txt



불행하게도 거기에는 분명한 것이 나타나지 않았지만 C++ 진입점은 데이터베이스 이론을 확고히 했습니다.

 [C++ entry points]:
   ticks cpp total name
   3497 72.1% 58.8% T __ZN2v88internal21Builtin_HandleApiCallEiPmPNS0_7IsolateE
   1290 26.6% 21.7% T 


HandleApiCall에서 72% - 우리가 만들고 있는 유일한 API 호출은 데이터베이스에 대한 것이므로 시작하기에 좋은 곳으로 보입니다.

기러기 추적



불행히도 이것은 내가 엉망인 곳입니다. 문제가 실제로 무엇인지 식별하는 것을 잊고 문제를 해결하기 시작했습니다.
  • knex-cleaner에서 knex("table").del()를 사용하여 개체를 개별적으로 삭제하는 것으로 변경 사항을 테스트하는 데 시간을 낭비했습니다.
  • 많은 테스트가 모든 것을 다시 시드하므로 시드에서 테이블 잘림 속도를 높이느라 시간을 낭비했습니다.
  • 테스트를 위해 PostgreSQL에서 SQLite로 변경을 시도했습니다
  • .
  • 시드 파일을 다시 쓰는 것에 대해 궁금해하기 시작했습니다
  • .

    거위를 쫓는 것을 멈췄다



    결국 나는 문제가 실제로 어디에 있는지 확인하기 위해 측정하는 것을 기억했습니다. 테스트가 아니라고 가정하면 cleanseed 두 단계가 있습니다. 어느 것이 문제인지 식별해 봅시다.

    exports.dbCleanAndSeed = async function () {
      let cleanFinishedMs, seedFinishedMs, startMs;
      const options = { ignoreTables: ["knex_migrations", "knex_migrations_lock"] };
      startMs = Date.now()
      await knexCleaner.clean(database, options);
      cleanFinishedMs = Date.now();
      await database.seed.run();
      seedFinishedMs = Date.now();
      console.log("Clean took: %i; seed took %i", cleanFinishedMs - startMs, seedFinishedMs - cleanFinishedMs);
    }
    
    


    트릭을 수행하고 내가 찾아야 할 곳을 알려주었습니다.

      site tests
    Clean took: 28; seed took 675
        ✓ can get the sites page (732ms)
    Clean took: 28; seed took 743
        ✓ get the 'add a new site page' (776ms)
    Clean took: 29; seed took 592
        ✓ add a new site (630ms)
    Clean took: 26; seed took 594
        ✓ add a site and see it on the sites page (628ms)
    Clean took: 29; seed took 748
        ✓ can't add a new site with no creds (779ms)
    Clean took: 27; seed took 652
        ✓ gets 404 for a site that doesn't exist (684ms)
    Clean took: 30; seed took 732
        ✓ can't add a new site with no domain (769ms)
    Clean took: 26; seed took 609
        ✓ can't add a new site with no active value (640ms)
    
    


    괜찮아. 따라서 청소는 확실히 문제가 아닙니다!

    그래도 5개의 시드 파일이 있습니다. 특히 어떤 파일이 문제인지 궁금하십니까? knex에 대한 소스 코드가 있으므로 시드 로딩 비트를 찾고 계측하기 쉬운지 확인하겠습니다.
    _waterfallBatch()knex/lib/seed/Seeder.js가 실제로 시드를 로드하는 기능인 것처럼 보이므로 거기에서 타임 스탬프를 수행해 보겠습니다.

      async _waterfallBatch(seeds) {
        const { knex } = this;
        const log = [];
        for (const seedPath of seeds) {
          const importFile = require('../util/import-file'); // late import
          const seed = await importFile(seedPath);
          try {
            const startMs = Date.now()
            await seed.seed(knex);
            const endMs = Date.now()
            console.log(`${seedPath} took ${endMs - startMs} ms`);
    [...]
    
    


    실행…

    seeds/01_user_seed.js took 147 ms
    seeds/02_site_seed.js took 6 ms
    seeds/03_review_seed.js took 3 ms
    seeds/04_campaign_seed.js took 5 ms
    seeds/05_redirect_seed.js took 461 ms
    
    


    … 오. 예, 꽤 결정적입니다.

    리디렉션 파일을 보면 유력한 범인이 보입니다.
    05_redirect_seed.js :

      const geoIpData = await iplocate(remoteIp);
    
    


    로드된 모든 단일 시드에 대한 Ageo IP lookup. 그렇게 할 것입니다.
    01_user_seed.js :

        return knex('users').insert([
            {email: '[email protected]',
             passwordHash: Bcrypt.hashSync("Sherlock",
             parseInt(process.env.BCRYPT_SALT_ROUNDS))
            }])
    
    


    그리고 삽입된 모든 사용자에 대한 bcrypt 해시. 그것은 그것을 설명할 것입니다.

    결론



    사용자 시드의 경우 Bcrypt 솔트 라운드를 1로 줄였습니다. 로컬 테스트이므로 공격에 저항할 필요가 없습니다.

    리디렉션을 위해 시드 파일에 사용된 IP에 대한 조회 테이블을 구현했습니다. 결과는 즉시 볼 수 있습니다.

    seeds/01_user_seed.js took 9 ms
    seeds/02_site_seed.js took 5 ms
    seeds/03_review_seed.js took 5 ms
    seeds/04_campaign_seed.js took 5 ms
    seeds/05_redirect_seed.js took 8 ms
    
    




    그게 끝입니다. 다른 사람을 돕는 희망!

    좋은 웹페이지 즐겨찾기