-
Notifications
You must be signed in to change notification settings - Fork 37
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
Make DefaultAggregateStore picklable #383
Merged
Merged
Changes from 9 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
a3b25cf
Make default aggregate class picklable
kidrahahjo 1bb1c85
Refactor pickling
kidrahahjo 3b3c075
Refactor code comments
kidrahahjo a76bfaf
Add __getstate__, __setstate__ methods to FlowProject
kidrahahjo 0fc6ed5
Make cloudpickle a hard dependancy
kidrahahjo 0396e70
Fix linting issues
kidrahahjo 5eb5236
Resolve merge conflicts
kidrahahjo 3941eea
Merge remote-tracking branch 'origin/next' into picklable_aggregatestore
bdice 901e03c
Minor changes to variable names.
bdice 5fc5e4c
Use f-string.
bdice 493521f
Solve circular reference issue during unpickling by pre-computing has…
bdice 2b6843f
Cache actual hash value.
bdice File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Do we still need to define these methods? I thought that using cloudpickle might make this unnecessary.
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 ran some tests and wanted to update my question. I don't think this block of code is needed if we're using
cloudpickle
, since the only problem I'm aware of is an issue with pickling locally defined functions (which cloudpickle solves). If that's correct, we shouldn't need to manually define__getstate__
and__setstate__
for any classes.@b-butler @kidrahahjo Is this correct? If not, could you help provide a minimal case where these are required and commit it to the tests?
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 project attribute gets lost if we don't get them manually. Not sure why is this happening though.
The error can be reproduced by executing this
pytest tests/test_project -k parallel -x
when these__getstate__
and__setstate__
methods are removed.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 spent quite a bit of time and determined the core issue. The problem has to do with pickling/unpickling the project configuration. We monkey-patch the signac project configuration in signac-flow and when that gets unpickled, it points to the
configobj
from signac. Since theflow
key isn't defined, it can't re-construct the project configuration during unpickling.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 think we can adopt this workaround with
__getstate__
/__setstate__
for now, and add a comment describing the core issue. I'll add that to this PR and approve it.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 gets even better. I got deeper into the problem, and found a cleaner solution. Here's my best explanation:
Recall that the FlowProject owns aggregate stores, which may include a
_DefaultAggregateStore
that holds a reference to the original FlowProject. The circular reference is not intrinsically a problem, but there is a complication. The_DefaultAggregateStore
is used as a key in a dict, which means that it must be hashable. When the__hash__
method is called during unpickling, the_DefaultAggregateStore
's hash function returnshash(repr(self._project))
. This means that in order to reconstruct the pickled project, the FlowProject (because of the_DefaultAggregateStore
) must compute its own__repr__
. However, that relies onFlowProject._config
already being set, since therepr
returns something likeProject.get_project('/path/to/project')
via theProject._rd()
method, which requires the root directory from the config. The_DefaultAggregateStore
instance cannot be used as a dict key in theFlowProject
(perhaps in_aggregator_per_group
or_stored_aggregates
?) because it's not hashable until theFlowProject
is fully initialized.If it were possible to control the order in which data was unpickled, it might be possible to break this circular chain and avoid the error. However, rather than defining
__getstate__
and__setstate__
for theFlowProject
or_DefaultAggregateStore
, I think it would be easiest just to storeself._hash_repr = repr(self._project)
when the_DefaultAggregateStore
is created, and define it__hash__
method to returnhash(self._hash_repr)
. It ends up being only 2 lines (instead of adding entirely new methods) and I think it might be safer and more future-proof.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.
@kidrahahjo @b-butler Tagging you both so you can see the above comment. I think this PR is good to go, now that I've fixed the issue described above. Is my explanation clear? Did I leave a good enough comment in the new code?