-
Notifications
You must be signed in to change notification settings - Fork 585
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
Forward pointers implementation #651
Conversation
5af7701
to
0917cc1
Compare
arctic/store/_ndarray_store.py
Outdated
{'$set': segment, | ||
'$addToSet': {'parent': version['base_version_id']}}, | ||
upsert=True) | ||
if ARCTIC_FORWARD_POINTERS is FwPointersCfg.DISABLED: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this block clearer if written as two optional write operations?, the second collection.update_one is rewritten but essentially the same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to favor some code duplication in favor of minimizing the regression risks, and keep the existing code path (with disabled fw pointers) as similar as possible.
We can properly refactor this once we build confidence in our implementation.
I am adding a todo with your suggestion though.
arctic/store/_ndarray_store.py
Outdated
segments = [x['segment'] for x in collection.find({'symbol': symbol, 'parent': parent_id}, | ||
projection={'segment': 1}, | ||
)] | ||
segments = [x['segment'] for x in collection.find(spec, projection={'segment': 1})] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worth stripping _id?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handy debug info indeed, added now
arctic/store/_ndarray_store.py
Outdated
'segment': {'$lt': to_index} | ||
} | ||
if from_index is not None: | ||
spec['segment']['$gte'] = from_index | ||
else: | ||
segment_count = version.get('segment_count', None) | ||
|
||
# We want to use FW pointers to read data if: | ||
# ARCTIC_FORWARD_POINTERS is currently enabled/hybrid AND last version was written with FW pointers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider making enabled use the reverse pointers to read, but then checking that the forward pointers reconcile? As a way of validating for now?
Just an idea, I'm not against this approach, just asking what we want to test during the hybrid mode period.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The written data are being verified upon write, i.e. we count the segments matching the write spec which have been actually written.
https://github.com/dimosped/arctic/blob/forward_pointers_final/arctic/store/_ndarray_store.py#L505
Tbh I am not against this idea, but maybe as a separate option/flag and add it as part of the verification check:
https://github.com/dimosped/arctic/blob/forward_pointers_final/arctic/store/_ndarray_store.py#L505
It would also mean r/w will be slower with an extra step of 1-query collecting N-document IDs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually went ahead and implemented this, but during tests which force a failed check, a nasty bug was uncovered.
If mongo_retry operation fails once, the successfully written segments always remain (it is Arctic's design decision which won't change now). The upserted ids though in subsequent retried, don't include them, breaking the model.
Working on it and will add explicit tests.
Looks great to me, but I don't consider myself qualified to approve |
I dont have an issue with this PR - this is more a general critique - rather than using env variables (we have quite a few of them now, all defined in different files) can we also support a YAML file? the default would be env vars, but if any of the values were also defined in the yaml file, those would take precedence? |
Building on @bmoscon point, some of these flags could be configured differently per connection. e.g. in the case of a backwards incompatible flag, a migration script might need to configure two instances with different flag values. |
I was also thinking to refactor and create a yaml or "arcitc/config.py" module which holds all configurations. |
maybe we could even have instead in config.py a python dict with default values. I always prefer plain python dicts over yaml when possible, and given all configs are exposed via env variables, they don't require a code re-release with updated values. @yschimke @bmoscon @jamesblackburn @willdealtry any objections with the above? |
@dimosped no objections, it might be best done as a separate PR and move everything over for consistency. |
@yschimke agreed, I will add even more config options in the next PR for the internal async work, so it would be best to refactor then, with the next PR. |
thanks to @yschimke suggestion for reconciliation option, @bmoscon @jamesblackburn this was a nasty one over with partial segment writes between mongo_retries: Need to also correct/update the versoins/segments garbage collection, and make it compatible also for versions which were created with forward pointers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Dimos - tricky change! One thing is to try to be as forwards compatible as possible. IMO new versions of arctic should be able to read either pointer scheme without environment variables being set... Similarly new versions of arctic shouldn’t corrupt existing libraries / writes.
I wonder if we need a version scheme on the library or similar, so we can raise
early if the arctic version is too old to read data correctly?
Will need some battle testing!
arctic/store/_ndarray_store.py
Outdated
# If this is the first use of fw-pointers, query for the full list of segment IDs, | ||
# or continue by extending the IDs from the previous version | ||
if previous_version and FW_POINTERS_KEY not in previous_version: | ||
segment_ids = {_id for _id in collection.find({'symbol': symbol, 'parent': version_base_or_id(version)}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth asserting here the segment id count is right (as is done in the read path)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check_written has been updated to make this check both for fw pointers and legacy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, I modified slightly the query to make it targeted for the version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yschimke @jamesblackburn @bmoscon I added a check here.
James has a point, because the check_written is only called for writes, not appends, so it is safer to double check here.
I am now marking this conversation as resolved.
arctic/store/_ndarray_store.py
Outdated
# Reason is that if only some of the segments might get written, and we re-execute write() via mongo_retry, | ||
# the upserted_id(s) won't be a complete list of all segment ObjectIDs | ||
shas_to_ids = {x['sha']: x['_id'] | ||
for x in collection.find({'symbol': symbol}, projection={'sha': 1, '_id': 1})} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won’t be covered by an index, will it? Also looks quite expensive as you’re iterating over every chunk for a symbol - and mongo will thrash the WT cache as it loads each into memory...
Should we be using shas instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will check this tomorrow with an explain() and see what happens because _id is quite special, and we have already an index for sha.
If this proves indeed to be expensive, I will consider the switch to shas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before sharing the explain() results, some benchmarks vs this:
https://github.com/manahl/arctic/blob/master/arctic/store/_ndarray_store.py#L515-L519
I assume the extra time is only for the transfer of the ObjectIDs and execution is acceptably fast.
The extra ms IMHO in almost a second of total time for the read-query, can be considered negligible.
Will share the explain() findings shortly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jamesblackburn I am not certain, but I would expect (NO-EVIDENCE here) MongoDB is smart enough to have a light in-memory structure to maintain the relation of an index to _id values, i.e. not having to iterate and really fetch the documents, simply for fetching the IDs, given you have are hitting only fields which are part of an index.
Do we have a way to evaluate the WT cache-pollution effects ? @rob256 @bmoscon any ideas here ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, @jamesblackburn look how the projection changes the index used between the first and the last queries.
This raises some suspicion that WT cache might indeed get polluted.
When only sha is in the projection, it selects the symbol_sha index, while when _id is in the projection, it picks the symbol_hashed index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed,
eqlib._collection.find({'symbol': symbol}, projection={'sha': 1, '_id': 1}).explain()
has a FETCH stage,
while this doesn't
eqlib._collection.find({'symbol': symbol}, projection={'sha': 1, '_id': 0}).explain()
It is quite obvious that @jamesblackburn intuition is correct, we would be polluting the cache, an d it is a good idea to convert to using SHAs instead, or add an index (symbol, sha, _id).
I'd rather use SHA though for obvious reasons
arctic/store/_ndarray_store.py
Outdated
projection={'sha': 1, '_id': 0}, | ||
) | ||
]) | ||
if ARCTIC_FORWARD_POINTERS is FwPointersCfg.DISABLED: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again shouldn’t this be based on precious_version, not on the environment variable?
I’m worried that people / code writing to different libraries with different env variables set will leave the versions in a mess?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have already updated in my last commit this, be based whenever makes sense on the previous version configuration.
arctic/store/version_store.py
Outdated
) | ||
return [version["base_version_id"] for version in cursor] | ||
|
||
def _prune_previous_versions(self, symbol, keep_mins=120, keep_version=None): | ||
def _prune_previous_versions(self, symbol, keep_mins=0.01, keep_version=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you turning keep_mins down?
This is / was needed to handle replication lags etc. Need to ensure this is larger than the maximum lag, or secondary reads can fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ignore this, I have set it for testing the pruning, when the PR reaches its final form, I will revert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will resolve this conversation once I switch it back.
@jamesblackburn @yschimke my last commit made this change fully forward/backwards compatible between enabled-fw-pointers/legacy-parent-pointers/hybrid. Tomorrow I will finalize the pruning, with respect to the last change for compatibility. I will also address the comments |
@jamesblackburn @yschimke forgot to mention that an extra aim was to make this also possible/supported:
|
LGTM - since this is optional, I suggest landing this and adding the tests as next PR. It's a big PR already. |
… with only subset of segments written
…nd being also backwards compatible
…ABLED/DISABLED/HYBRID per write/append/read
…, and raise operation errors upon failure
a43e2e8
to
ec4adee
Compare
…in the version metadata
…fixed multiple tests after enabling for all VersionStore tests to run in all three modes for forward pointers
…ng with FW pointers too early, as the version is not inserted yet when pruning happens
…ed in the past than 24h. Added fw pointers in multiple other tests to verify functionality
….Binray comparison with binary
🥳 |
This reverts commit 6735c04.
Solves #650, #547 and #663
Implement 3 globally configured env variables: