Skip to content
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

File caching for the oauth2client >= 4.0.0 #325

Closed
xmedeko opened this issue Jan 8, 2017 · 14 comments
Closed

File caching for the oauth2client >= 4.0.0 #325

xmedeko opened this issue Jan 8, 2017 · 14 comments
Assignees
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@xmedeko
Copy link
Contributor

xmedeko commented Jan 8, 2017

@tmatsuo proposed in #110 to cache discovery.build(), however, #294 has stopped using cache outside of the GAE (appengine) environment (see also #299). E.g. the discovery.build() is not cached in the CGE environment. Is it possible to bring the file caching back, or provide some example of file cache (subclass of googleapiclient.discovery_cache.Cache)?

@theacodes
Copy link
Contributor

theacodes commented Jan 9, 2017 via email

@Schweigi
Copy link

Is there a known workaround in the meantime?

@Schweigi
Copy link

Schweigi commented Jan 22, 2017

I created a small workaround by using a in memory cache (see example below). Does anyone know if there should be a regular cache invalidation? Currently the memory cache is cleared on each deployment, I hope that's ok for the time being.

In memory cache:

class MemoryCache(Cache):
    _CACHE = {}

    def get(self, url):
        return MemoryCache._CACHE.get(url)

    def set(self, url, content):
        MemoryCache._CACHE[url] = content

Usage:

http = ...
service = discovery.build('calendar', 'v3', http=http, cache=MemoryCache())

@xmedeko
Copy link
Contributor Author

xmedeko commented Jan 23, 2017

AFAIK the Google production API should not change (the same version of API), so the you do not need to invalidate the cache. However, the discovery_cache/__init__.py defines the limit DISCOVERY_DOC_MAX_AGE as one day.

+1 for a neat, simple MemoryCache. IMO it's good when you have a single, long running process.

If you want to share the cache among processes, (like running scripts by the cron,) you may copy the old good oauth2client locked_file.py and then to plug it somehow with discovery_cache/file_cache.py. Beware, the locked_file.py is considered not 100% save, so you take the risk. (But works well in practice.) By "plugin somehow" I mean either copy & change the discovery_cache/file_cache.py to take your locked_file.py or to trick the Python to think your locked_file.py is from the oauth2client.locked_file package.

@xmedeko
Copy link
Contributor Author

xmedeko commented May 17, 2017

@jonparrott what about to change file lock to the multiprocessing.RLock() in the original locked_file.py? Would be it sufficient solution to accept back to the repo?

@theacodes
Copy link
Contributor

theacodes commented May 17, 2017 via email

@xmedeko
Copy link
Contributor Author

xmedeko commented May 17, 2017

You mean a file cache, shared among multiple processes, without any locking? How to prevent data corruption, when 2 process write to the same file?

@theacodes
Copy link
Contributor

theacodes commented May 17, 2017 via email

@theacodes theacodes removed their assignment Oct 10, 2017
@JustinBeckwith JustinBeckwith added 🚨 This issue needs some love. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. and removed status: enhancement labels Jun 8, 2018
@xmedeko
Copy link
Contributor Author

xmedeko commented Jun 19, 2018

FYI to use the locked_file from oauth2client 3.0.0 without that package (it's not required since 1.7.3), just use the gist https://gist.github.com/xmedeko/e5293707e075d18c9dde8a574d2670e5 and call the shim() once.

@JustinBeckwith JustinBeckwith added priority: p2 Moderately-important priority. Fix may not be included in next release. and removed priority: p2+ labels Jul 16, 2018
@Chronial
Copy link

Chronial commented Sep 7, 2018

Based on Schweigi's solution, I wrote a simple file based cache (unix-only) to also cache across processes.

import os.path
import hashlib
import tempfile

class DiscoveryCache:
    def filename(self, url):
        return os.path.join(
            tempfile.gettempdir(),
            'google_api_discovery_' + hashlib.md5(url.encode()).hexdigest())

    def get(self, url):
        try:
            with open(self.filename(url), 'rb') as f:
                return f.read().decode()
        except FileNotFoundError:
            return None

    def set(self, url, content):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            f.write(content.encode())
            f.flush()
            os.fsync(f)
        os.rename(f.name, self.filename(url))

@busunkim96
Copy link
Contributor

I'm opting to close this as it's unlikely we will implement it in the future.

These client libraries are officially supported by Google. However, the libraries are considered complete and are in maintenance mode. This means that we will address critical bugs and security issues but will not add any new features.

cjoshmartin added a commit to cjoshmartin/e-paper-calender that referenced this issue Feb 2, 2020
b4tman added a commit to b4tman/sync_ics2gcal that referenced this issue Feb 25, 2020
to suppress errors in logs:
file_cache is unavailable when using oauth2client >= 4.0.0

googleapis/google-api-python-client#299
googleapis/google-api-python-client#325
@aawilson
Copy link

I don't know if this changed at some point, but I had to import Cache from googleapiclient.discovery_cache.base (presumably googleapiclient.discovery_cache.file_cache also works fine, but inheriting from the Base class is almost certainly the better idea)

aawilson added a commit to aawilson/python-docs-samples that referenced this issue Jun 26, 2020
The original main.py creates noisy log messages when the function is run file_cache is unavailable when using oauth2client >= 4.0.0:

```
2020-06-26T19:34:03.459Z datastore_export file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export Traceback (most recent call last): E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 33, in <module> E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     from oauth2client.contrib.locked_file import LockedFile E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export ModuleNotFoundError: No module named 'oauth2client' E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export During handling of the above exception, another exception occurred: E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export Traceback (most recent call last): E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 37, in <module> E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     from oauth2client.locked_file import LockedFile E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export ModuleNotFoundError: No module named 'oauth2client' E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export During handling of the above exception, another exception occurred: E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export Traceback (most recent call last): E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/__init__.py", line 44, in autodetect E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     from . import file_cache E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 41, in <module> E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     "file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth" E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth E  datastore_export 
```

While they don't interfere with the function's operation, they are disconcerting and not necessary. This implements the workaround suggested in googleapis/google-api-python-client#325 (which creates an in-memory cache object to use in replacement).
leahecole added a commit to GoogleCloudPlatform/python-docs-samples that referenced this issue Jul 1, 2020
* fix: Replace default file_cache usage with simple memory cache

The original main.py creates noisy log messages when the function is run file_cache is unavailable when using oauth2client >= 4.0.0:

```
2020-06-26T19:34:03.459Z datastore_export file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export Traceback (most recent call last): E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 33, in <module> E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     from oauth2client.contrib.locked_file import LockedFile E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export ModuleNotFoundError: No module named 'oauth2client' E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export During handling of the above exception, another exception occurred: E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export Traceback (most recent call last): E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 37, in <module> E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     from oauth2client.locked_file import LockedFile E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export ModuleNotFoundError: No module named 'oauth2client' E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export During handling of the above exception, another exception occurred: E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export  E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export Traceback (most recent call last): E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/__init__.py", line 44, in autodetect E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     from . import file_cache E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export   File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 41, in <module> E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export     "file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth" E  datastore_export 
2020-06-26T19:34:03.459Z datastore_export ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth E  datastore_export 
```

While they don't interfere with the function's operation, they are disconcerting and not necessary. This implements the workaround suggested in googleapis/google-api-python-client#325 (which creates an in-memory cache object to use in replacement).

* Add explanatory comment

Add a comment explaining the inclusion of MemoryCache instance in the `build` invocation.

* Update main.py

PEP8-mandated two newlines between top-level elements

Co-authored-by: Christopher Wilcox <crwilcox@google.com>
Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com>
@Kache
Copy link

Kache commented Sep 20, 2021

An equivalent & simpler workaround is to set cache_discovery=False:

discovery.build(api, version, http=http, cache_discovery=False)

Since according to the source the "cache" is never reused in the "single, long running process" case.

@neomafo88
Copy link

neomafo88 commented Nov 27, 2022

I created a small workaround by using a in memory cache (see example below). Does anyone know if there should be a regular cache invalidation? Currently the memory cache is cleared on each deployment, I hope that's ok for the time being.

In memory cache:

class MemoryCache(Cache):
    _CACHE = {}

    def get(self, url):
        return MemoryCache._CACHE.get(url)

    def set(self, url, content):
        MemoryCache._CACHE[url] = content

Usage:

http = ...
service = discovery.build('calendar', 'v3', http=http, cache=MemoryCache())

googleapiclient.discovery_cache.base.Cache

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests