From 1b73957be23fea29a8a67151dbd0449daf914440 Mon Sep 17 00:00:00 2001 From: Slava Kim Date: Mon, 10 Aug 2015 19:35:44 -0700 Subject: [PATCH 1/3] Add more info on how Minimongo works --- packages/minimongo/README.md | 70 +++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/minimongo/README.md b/packages/minimongo/README.md index b3589e5a9d..d91dc2f6b8 100644 --- a/packages/minimongo/README.md +++ b/packages/minimongo/README.md @@ -1,4 +1,4 @@ -# minimongo +# Minimongo `minimongo` is reimplementation of (almost) the entire MongoDB API, against an in-memory JavaScript database. It is like a MongoDB emulator that runs inside @@ -11,4 +11,72 @@ Minimongo is used as a temporary data cache in the standard Meteor stack, to learn more about mini-databases and what they can do, see [the project page on www.meteor.com](https://www.meteor.com/mini-databases) +## Internals + +Minimongo implements the following features, mirroring the MongoDB features: + +- Selectors +- Modifiers +- Fields projections +- Querying with `sort` and `limit` +- ObjectID generation +- Geo-positional operator `$near` with GeoJSON parsing + +Internally, all documents are mapped in a single JS object from `_id` to the +document. Besides this mapping, Minimongo doesn't implement any types of +secondary indexes. + +Also, currently Minimongo doesn't implement any aggregation features. The full +list of incompatible features can be found in the NOTES file. + +Besides the MongoDB features, Minimongo implements the following for a better +integration with the Meteor stack: + +- `observe` and `observeChanges` APIs, notification callbacks when the result of +a query changes +- integration with Meteor's [Tracker](https://www.meteor.com/tracker) +- Meteor's Publish a cursor interface `_publishCursor`, that results into an + `observe` call +- Meteor's interface of `pauseObservers` and `resumeObservers`, for client-side + caches (allow latency compensation to change multiple objects at once w/o a + flicker) +- In addition to the previous point, `saveOriginals` and `retrieveOriginals` are + used to revert the local changes +- additional analysis functions for Meteor's server-side Oplog Observe Driver +(only loaded for the server-side): + * can this modifier change the result of this selector if the document didn't + match before + * can the sort order change after applying this modifier (for a given + selector) + * what is a combined projection for these selector, sorter and projection + + +### saveOriginals/retrieveOriginals & pauseObserver/resumeObservers + +This part of the implementation is very important for a smooth Optimistic UI +experience (also called "Latency Compensation") avoiding an extra flicker. + +Let's review the usual Optimistic UI flow: + +- User triggers an interaction on the client +- The simulation applies some mutations to the state locally +- The RPC is fired to be executed on the server +- After some time, the RPC returns with the result +- After some more time, RPC returns an "updated" message (on DDP level), meaning +that all the changes from RPC have persisted +- At this point we know, that all the actual changes from the server are synced, + we can throw away the simulated mutations (preserving the real changes from + the server) + +To implement the last step, Minimongo implements the following: + +- When a simulation starts, `saveOriginals` is called, to take a snapshot of the +state before the update. Internally, it is implemented in a more efficient +copy-on-write style. +- After the simulation, wait, to get all the real changes from the server. +- When the server is done with giving us new changes, we apply all the new +changes and throw away all the simulated changes (with `retrieveOriginals`). +- All massive replacements happen in between `pauseObservers` and + `resumeObservers` calls, so any user code (such as Blaze) sees the whole + change in one tick (helps to avoid the flicker). From c05f0aca564eb8d402a281cf35865baaec8fa340 Mon Sep 17 00:00:00 2001 From: Anubhav Jain Date: Mon, 10 Aug 2015 19:55:52 -0700 Subject: [PATCH 2/3] Revert "Add checks to see if frame exists before trying to access the func property" This reverts commit bd6a0c00803bf6f8223ed06f3e3e97fa935cae6a. --- tools/utils/buildmessage.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/utils/buildmessage.js b/tools/utils/buildmessage.js index cfb1b1c0f3..650cf8e2b8 100644 --- a/tools/utils/buildmessage.js +++ b/tools/utils/buildmessage.js @@ -92,7 +92,7 @@ _.extend(Job.prototype, { // If a nontrivial stack trace (more than just the file and line // we already complained about), print it. var where = ""; - if (frame && frame.file) { + if (frame.file) { where += frame.file; if (frame.line) { where += ":" + frame.line; @@ -102,11 +102,11 @@ _.extend(Job.prototype, { } } - if (frame && ! frame.func && ! where) + if (! frame.func && ! where) return; // that's a pretty lame stack frame line += " at "; - if (frame && frame.func) + if (frame.func) line += frame.func + " (" + where + ")\n"; else line += where + "\n"; From 863be787ab742bd57fdee097c87f2471f5d75d10 Mon Sep 17 00:00:00 2001 From: Sashko Stubailo Date: Mon, 10 Aug 2015 20:07:52 -0700 Subject: [PATCH 3/3] Fix buildmessage concatenating stack traces --- tools/tests/parse-stack-test.js | 13 ++++++++++--- tools/utils/buildmessage.js | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/tests/parse-stack-test.js b/tools/tests/parse-stack-test.js index b72700eabf..920eca8339 100644 --- a/tools/tests/parse-stack-test.js +++ b/tools/tests/parse-stack-test.js @@ -15,12 +15,19 @@ selftest.define("parse-stack - parse stack traces without fibers", () => { markBottom(() => { const markedErr = new Error(); - const parsedStack = parse(markedErr).outsideFiber; + + const { + outsideFiber, + insideFiber + } = parse(markedErr); + + // Don't return an empty array + selftest.expectEqual(insideFiber, undefined); // The stack trace should only contain this one function since we marked the // bottom - selftest.expectEqual(parsedStack.length, 1); - selftest.expectEqual(_.last(parsedStack[0].file.split("/")), + selftest.expectEqual(outsideFiber.length, 1); + selftest.expectEqual(_.last(outsideFiber[0].file.split("/")), "parse-stack-test.js"); })(); }); diff --git a/tools/utils/buildmessage.js b/tools/utils/buildmessage.js index 650cf8e2b8..cbfda41c61 100644 --- a/tools/utils/buildmessage.js +++ b/tools/utils/buildmessage.js @@ -439,7 +439,7 @@ var error = function (message, options) { } = parseStack.parse(new Error()); // Concatenate and get rid of lines about Future and buildmessage - info.stack = outsideFiber.concat(insideFiber).slice(2); + info.stack = outsideFiber.concat(insideFiber || []).slice(2); if (typeof info.useMyCaller === 'number') { info.stack = info.stack.slice(info.useMyCaller); }