-
Notifications
You must be signed in to change notification settings - Fork 4
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
Use get_records_with_cache
to cache to_records
calls
#286
Conversation
After thinking more about this, possibly a better solution would be for us (or actually a user) to subclass |
Here's an example of the import os
import shutil
from qcportal import PortalClient
from qcportal.cache import RecordCache, get_records_with_cache
from qcportal.optimization import OptimizationRecord
from openff.qcsubmit._tests.utils.test_manager import no_internet
from openff.qcsubmit.results import OptimizationResultCollection
from openff.qcsubmit.utils.utils import portal_client_manager
class CachedPortalClient(PortalClient):
def __init__(self, addr, cache_dir, **client_kwargs):
super().__init__(addr, cache_dir=cache_dir, **client_kwargs)
self.record_cache = RecordCache(
os.path.join(self.cache.cache_dir, "cache.sqlite"), read_only=False
)
def get_optimizations(self, record_ids, missing_ok=False, include=None):
return get_records_with_cache(
self,
self.record_cache,
OptimizationRecord,
record_ids,
include=["initial_molecule", "final_molecule"],
)
if os.path.exists("api.qcarchive.molssi.org_443"):
shutil.rmtree("api.qcarchive.molssi.org_443")
opt = OptimizationResultCollection.parse_file("tiny-opt-dataset.json")
client = CachedPortalClient("https://api.qcarchive.molssi.org:443", cache_dir=".")
with portal_client_manager(lambda _: client):
client._req_session = None
with no_internet():
print(len(opt.to_records())) This code runs on the branch for #284 without any changes to qcsubmit. The implementation of |
I've updated the implementation to use the I have not added any tests specifically for the caching yet, but I think it's a great first sign that the tests pass after replacing the default One issue I've run into with testing this is that each test uses the same default |
The tests should now be passing with temporary cache directories everywhere Now, I think I just need to update these tests to check that the caching actually worked. |
The tests have been updated, and I copied over (and modified) documentation from qcportal. It should be ready for review! |
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 @ntBre. We reviewed this during our check in this week and I summarized our discussion in the comments. This is good to merge once the (blocking) items are resolved and the releasenotes are updated!
openff/qcsubmit/utils/utils.py
Outdated
returns a list of records. | ||
""" | ||
if missing_ok: | ||
logger.warning("missing_ok provided but unused by CachedPortalClient") |
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.
(not blocking) Maybe something more like "missing_ok was set to True, but CachedPortalClient doesn't actually support this so it's being set to False"
openff/qcsubmit/utils/utils.py
Outdated
|
||
|
||
def _default_portal_client(client_address) -> PortalClient: | ||
return PortalClient(client_address) | ||
return CachedPortalClient(client_address, cache_dir=_DEFAULT_CACHE_DIR) |
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.
(blocking) Given that this may not actually be thread-safe, let's make caching not be the default. (or implement a test using multiprocessing or something to ensure that two different processes with different clients but the same cache dir don't trip each other up).
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.
Good call on this. This code fails with a database is locked
error:
from multiprocessing import Pool
from qcportal import PortalClient
from openff.qcsubmit.results import TorsionDriveResultCollection
from openff.qcsubmit.utils import CachedPortalClient, portal_client_manager
ds = TorsionDriveResultCollection.parse_file(
"/home/brent/omsf/projects/valence-fitting/02_curate-data/datasets/supp-td.json"
)
datasets = [ds, ds, ds, ds]
client = CachedPortalClient("https://api.qcarchive.molssi.org:443", cache_dir=".")
with portal_client_manager(lambda _: client), Pool(4) as pool:
for res in pool.imap_unordered(
TorsionDriveResultCollection.to_records, datasets, 1
):
print(len(res))
It also fails without the portal_client_manager
and CachedPortalClient
as the default client, so it's not safe to share the same client across threads or to construct multiple clients accessing the same database.
It actually doesn't quite work most of the time, even with a regular PortalClient
with or without a cache dir, but some of the processes have read
timeout errors rather than the more dramatic database errors from the cached version. It's probably best in general not to share a PortalClient
across threads.
Using with portal_client_manager(PortalClient), Pool(4) as pool:
(removing the lambda that shares the same client), as your suggestion will restore the default to, has worked successfully on every run I've tried so far, so I think that's definitely the right default.
Do you think it's worth mentioning something about this in the documentation for portal_client_manager
? The docs currently show an example of a function that returns a new PortalClient
each time (which should be safe) rather than a lambda _: client
. I'm not really sure how to warn against this without showing the lambda code in the docs, though.
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 for the update and for checking on multithreading. I've read this a few times and think I kinda understand, and my preference is to do the simplest safe thing. So I agree with reverting the default behavior. And updating the docs is optional, you might just copy your comment "It's not safe to share the same client across threads or to construct multiple clients accessing the same database" into the portal_client_manager docstring and call it a day.
Description
This PR uses the get_records_with_cache function discussed in our last QCArchive meeting to cache
to_records
calls automatically. With these changes alone, users cannot actually access this behavior, but when combined with #284, it enables code like this:For this PR, I applied the changes separately from #284 to the main branch, but I have used this code to test the combined changes locally, and it works! I have not run into MolSSI/QCFractal#844 here yet, so hopefully that is less common in real use cases.
Todos
to_records
callsStatus