[SERVER-41011] Server may crash when running $where on non-existent collection Created: 03/May/19  Updated: 15/Jul/19  Resolved: 29/May/19

Status: Closed
Project: Core Server
Component/s: Querying
Affects Version/s: 4.1.10
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Ian Boros Assignee: Xiangyu Yao (Inactive)
Resolution: Duplicate Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
Duplicate
duplicates SERVER-41041 ViewCatalog should actively reload af... Closed
Related
is related to SERVER-41041 ViewCatalog should actively reload af... Closed
Operating System: ALL
Sprint: Execution Team 2019-06-03
Participants:
Linked BF Score: 6

 Description   

As discovered by gregory.wlodarek, this will crash the server with an invariant when run on a secondary:

db.system.js.insertOne({x: 1});
db.createView('someView', 'anyBaseColl', []);
db.js_protection.find({$where: 'aaa'}).toArray();

resmoke --suites=jstestfuzz_sharded_causal_consistency repro.js

Here is the sequence of events that cause the find() command to trigger an invariant:
1) The find command makes an AutoGetCollectionForReadCommand on test.js_protection.

1a) The construction of the AutoGetCollection causes a view catalog lookup and thus, refresh because of the recently created view.

1b) The refresh causes us to read from system.views, which "activates" the recovery unit when the WiredTigerCursor is created. Note that this refresh only happens when the collection does not exist.

1c) After the AutoGetCollection is constructed This entire block of AutoGetCollectionForRead() is skipped because the collection does not exist.

2) We initialize the WhereMatchExpression which creates a JS scope. As part of creating the JS scope, we create a DBDirectClient for reading from system.js.

3) We run a find() query using the DBDirectClient, and enter FindCmd::Invocation::run() again. We create an AutoGetCollectionForReadCommand, but this time, on system.js.

3a) Inside of AutoGetCollectionForRead() we decide that since we're on a secondary and the readConcern is "local", we should read from the last applied timestamp.

3b) We try to set the recovery unit's timestamp read source to kLastApplied here, which triggers this invariant, because the recovery unit was made "active" during the view catalog lookup:

// wiredtiger_recovery_unit.cpp L786-L791
invariant(!_isActive() || _timestampReadSource == readSource,
          str::stream() << "Current state: " << toString(_state)
                        << ". Invalid internal state while setting timestamp read source: "
                        << static_cast<int>(readSource)
                        << ", provided timestamp: "
                        << (provided ? provided->toString() : "none"));

I could not repro this on 4.0 because in that branch, the invariant allows for _timestampReadSource to be kUnset. See here

invariant(!_active || _timestampReadSource == ReadSource::kUnset ||
         _timestampReadSource == readSource);



 Comments   
Comment by Gregory Wlodarek [ 15/Jul/19 ]

Verified by hand that SERVER-41041 did fix this.

Comment by Xiangyu Yao (Inactive) [ 29/May/19 ]

Discussed with milkie and we decided not to change the findCmd behavior. Marking this as a dup of SERVER-41041 as SERVER-41041 will fix this issue.

Comment by Xiangyu Yao (Inactive) [ 28/May/19 ]

Seems the current behavior is that we continue findCmd even if the collection does not exist. code

Comment by Xiangyu Yao (Inactive) [ 21/May/19 ]

I think the problem can be solved by SERVER-41041 because 1b) won't happen and thus not activating a recovery unit. But I am still curious why it continued running the $where code (step 2) after it found out that the collection ("js_projection") didn't exist.

Generated at Thu Feb 08 04:56:35 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.