Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: another attempt at fixing the flaky BulkWriter test #1228

Merged
merged 5 commits into from
Jun 19, 2020

Conversation

thebrianchen
Copy link

@thebrianchen thebrianchen commented Jun 18, 2020

No description provided.

@thebrianchen thebrianchen self-assigned this Jun 18, 2020
@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Jun 18, 2020
@codecov
Copy link

codecov bot commented Jun 18, 2020

Codecov Report

Merging #1228 into node10 will decrease coverage by 0.17%.
The diff coverage is 93.06%.

Impacted file tree graph

@@            Coverage Diff             @@
##           node10    #1228      +/-   ##
==========================================
- Coverage   98.59%   98.42%   -0.18%     
==========================================
  Files          28       30       +2     
  Lines       18301    18474     +173     
  Branches     1403     1423      +20     
==========================================
+ Hits        18044    18183     +139     
- Misses        254      288      +34     
  Partials        3        3              
Impacted Files Coverage Δ
dev/src/index.ts 97.51% <71.05%> (-0.67%) ⬇️
dev/src/util.ts 96.85% <91.42%> (-1.32%) ⬇️
dev/src/bulk-writer.ts 98.86% <100.00%> (+0.43%) ⬆️
dev/src/document.ts 98.77% <100.00%> (-0.01%) ⬇️
dev/src/path.ts 98.55% <100.00%> (-0.01%) ⬇️
dev/src/reference.ts 99.88% <100.00%> (+<0.01%) ⬆️
dev/src/transaction.ts 97.03% <100.00%> (ø)
dev/src/write-batch.ts 100.00% <100.00%> (ø)
.prettierrc.js 0.00% <0.00%> (ø)
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 13871d9...43a0fac. Read the comment docs.

// there are floating promises, not waiting here can cause a race
// condition where the first batch's request has not been made by the
// time the second request is sent.
await requestMadeDeferred.promise;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little confused here... isn't the test meant to send off a lot of writes as fast as possible, and as such it would be beneficial not to wait?

It seems like what we should do is:

setTimeoutHandler((_, timeout) => {
  if (!timeoutCalled) {
    timeoutCalled = true;
    // expect(requestCounter).to.equal(0); This is racy without your 
   //  deferred promise, but not needed for the purpose of the test
    expect(timeout).to.be.greaterThan(0);
    // done(); Maybe remove this so we don't have any outstanding 
    //  work when the test completes.
  }
});

for (let i = 0; i < 600; i++) {
   bulkWriter.set(firestore.doc('collectionId/doc' + i), {foo: 'bar'});
}

await bulkWriter.close();
expect(timeoutCalled).to.be.true;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post conversation:
The original issue with this simplified test was that it still passes even if the timeout handler is called for the first batch. To fix this, the BulkWriter tests now check that the timeout handler wasn't called after each individual test.

I tried removing the done(), but since it's based on the actual clock, the timeout handler will be called multiple times. If we just call the timeout function directly in the overridden handler, we will loop until enough time passes for the rate limiter to allow the request. Though there could still be outstanding work once the test completes, it shouldn't affect other tests since each test creates a new BulkWriter instance.

@thebrianchen
Copy link
Author

I also removed the response generation logic since the rate limiter test isn't testing the request/response logic. This also allows the test to pass independently of what the maximum batch size is.

const responses = mergeResponses(
Array.from(new Array(requestLength), (_, i) => successResponse(i))
);
return response({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty great simplification.

it.skip('does not send batches if doing so exceeds the rate limit', async () => {
// The test is considered a success if BulkWriter tries to send the second
// batch again after a timeout.
afterEach(() => setTimeoutHandler(setTimeout));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably don't need this anymore since this is called above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

}
await bulkWriter.close();
if (!timeoutCalled) {
done(new Error('Expected the timeout handler to be called'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually an error to invoke done() twice. This should show up in the console.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either the done() in the timeout handler will be called, setting timeoutCalled to true, or the error done() will be called. Calling done() twice will cause the test to fail.

for (let i = 0; i < 600; i++) {
bulkWriter.set(firestore.doc('collectionId/doc' + i), {foo: 'bar'});
}
await bulkWriter.close();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't wait for the close to finish (i.e. no dangling promise in this test), then verifyInstance (line 189) might become flaky since it verifies that at the end of each test all operations have finished executing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

talked separately: The 2nd request will never fire since we are not invoking the callback function in the overridden handler. This means that the the handler will be trigger twice: once when the 2nd request is made, and once when the first request is returned.

const arrayRange2 = [500, 501, 502, 503, 504];
const requests2 = arrayRange2.map(i => setOp('doc' + i, 'bar'));
const responses2 = arrayRange2.map(i => successResponse(i));
it('does not send batches if doing so exceeds the rate limit', done => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final nit: This test should get a more obvious name "Uses timeout for batches that exceed the rate limit" for example.

@thebrianchen thebrianchen merged commit 2f417f7 into node10 Jun 19, 2020
@thebrianchen thebrianchen deleted the bc/bulk-flake branch June 19, 2020 19:00
schmidt-sebastian added a commit that referenced this pull request Jun 24, 2020
* fix!: mark v1beta1 client as deprecated (#937)

* feat!: use QueryDocumentSnapshot in FirestoreDataConverter (#965)

* deps: update to gts 2.x (#1013)

* chore!: update settings for Node 10 (#1019)

* deps: drop through2 (#1014)

* feat: support BigInt (#1016)

* fix: make update.sh work on Linux (#1043)

* fix: only use BigInt in BigInt system test (#1044)

* fix: make pbjs compile admin proto again (#1045)

* Add BulkWriter (#1055)

* docs: Add documentation for FirestoreDataConverter (#1059)

* chore: enforce return types (#1065)

* fix: add generic to Firestore.getAll() (#1066)

* chore: remove internal WriteOp (#1067)

* chore: add linter checks for it|describe.only (#1068)

* fix: handle terminate in BulkWriter (#1070)

* chore: run template copying last in synthtool (#1071)

* feat: Firestore Bundles implementation (#1078)

* feat: add support for set() with SetOptions when using FirestoreDataConverter (#1087)

* feat: Add totalDocuments and totalBytes to bundle metadata. (#1085)

* feat: Add totalDocuments and totalBytes to bundle metadata.

* fix: Better comment

* fix: Better testing.

* fix: Improve metadata testing.

* fix: incomplete expect in rate-limiter test (#1092)

* Remove BatchWrite proto, fix conformance tests

* chore: use public API types internally (#1100)

* feat: add Partition and BatchWrite protos (#1110)

* fix: remove GCF transaction fallback (#1112)

* fix: add BulkWriter integration tests, java backport changes, delete fix (#1117)

* chore: merge master (#1218)

* chore: add eslint check for console.log statements (#1229)

* fix: another attempt at fixing the flaky BulkWriter test (#1228)

* Fix comment

* Renames

* Test fix

* Fix unit tests

Co-authored-by: Brian Chen <chenbrian@google.com>
Co-authored-by: wu-hui <53845758+wu-hui@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants