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

In memory cache: unstored modifications are included in later gets #888

Open
snarfed opened this issue Jun 22, 2023 · 2 comments
Open

In memory cache: unstored modifications are included in later gets #888

snarfed opened this issue Jun 22, 2023 · 2 comments
Labels
api: datastore Issues related to the googleapis/python-ndb API.

Comments

@snarfed
Copy link

snarfed commented Jun 22, 2023

Hi all! I spent some time debugging this surprising behavior of ndb's in-memory cache today. I don't know if it's intentional or not, but it definitely caught me off guard, and I suspect it's not what you want. When I put an entity, then modify it in memory without puting it again, then get it, the returned entity includes the modifications that weren't put.

Example code below. I expect this happens because the in-memory cache stores and returns the exact entities it's given. The impact here is also probably not huge, since contexts are generally per request or otherwise local and short-lived, and this usage pattern is probably unusual. Still though, it's a nasty surprise. Should the cache maybe make copies of entities on both put and get to prevent this? It'd add overhead, but I wonder if it would be worth it to avoid subtle, difficult-to-debug bugs.

Environment details

MacOS and Linux, Python 3.9.16, ndb 2.1.1. Other libs:
google-api-core 2.11.0
google-auth 2.15.0
google-cloud-appengine-logging 1.3.0
google-cloud-audit-log 0.2.5
google-cloud-core 2.3.2
google-cloud-datastore 2.11.0
google-cloud-error-reporting 1.9.1
google-cloud-logging 3.5.0
google-cloud-ndb 2.1.1
google-cloud-tasks 2.13.1
googleapis-common-protos 1.59.0

Steps to reproduce

  1. Store an entity
  2. Modify the entity
  3. Don't put it again
  4. get the entity
  5. It has the unstored modifications

Code example

class Foo(Model):
    a = StringProperty()

f = Foo(id='x', a='asdf')
f.put()
print(id(f))

f.a = 'qwert'
got = Foo.get_by_id('x')
print(got)
print(id(got))

The output shows that got includes the modification of a = 'qwert' that was never written to the datastore, and that it's the same object in memory as f:

4393984640
Foo(key=Key('Foo', 'bar'), a='qwert')
4393984640

If I add use_cache=False to the get, or context.clear_cache() before it, I get the original object with 'asdf' for f, without the unstored modification.

Stack trace

N/A

cc snarfed/bridgy-fed#558 (comment)

@product-auto-label product-auto-label bot added the api: datastore Issues related to the googleapis/python-ndb API. label Jun 22, 2023
snarfed added a commit to snarfed/bridgy-fed that referenced this issue Jun 23, 2023
it has a bug/weird behavior that we want to avoid, at least for now.

googleapis/python-ndb#888
#558
@ventice11o
Copy link
Contributor

This also happens when a transaction fails. The values of failed transaction remain in the cache.

@snarfed
Copy link
Author

snarfed commented May 31, 2024

Interestingly this only happens with the context-local cache, not the global cache. If I run the same code above with ndb_client.context(global_cache=_InProcessGlobalCache()), the get returns the originally put entity, without the modification.

snarfed added a commit to snarfed/bridgy-fed that referenced this issue Oct 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: datastore Issues related to the googleapis/python-ndb API.
Projects
None yet
Development

No branches or pull requests

2 participants