Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Replace App Engine NDB with google cloud NDB. #1298

Merged
merged 27 commits into from
Jan 29, 2020
Merged

Replace App Engine NDB with google cloud NDB. #1298

merged 27 commits into from
Jan 29, 2020

Conversation

oliverchang
Copy link
Collaborator

@oliverchang oliverchang commented Dec 19, 2019

Part of migration to Python 3. This is our last major blocker and
dependency on the Python 2 App Engine SDK.

Also remove ndb_patcher, as that is no longer required.

Notable changes:

  • indexed=False is no longer supported for StringProperty. Use TextProperty instead, which is
    equivalent.
  • batch_size query keyword arg is no longer supported (Datastore backend change).
  • fetch_page() is no longer supported for complex queries with !=, OR etc.
  • Many import hacks/workarounds to make things work for App Engine.
    • Notable: add grpcio to app.yaml, cron-service.yaml
  • Add workaround for cursor_after() doesn't exist at end of iteration googleapis/python-ndb#292 and Can no longer pass property types to projection argument ? googleapis/python-ndb#295
  • Threads may be created by the new NDB implementation, which are only guaranteed to be
    stopped after a GC. GAE doesn't like it when there are stray threads at the end of a request.
  • Stub out ndb_utils.get_all_from_query. This is no longer required, and callers will be cleaned up in a future PR.
  • All datastore operations now require:
from datastore import ndb_init
with ndb_init.context():
  ...

This is automatically set up in tests, as well as in run_bot, run, heartbeat, run_heartbeat, and butler run, as well as for each individual request.

@oliverchang
Copy link
Collaborator Author

One more blocker: googleapis/python-ndb#292

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

oliverchang commented Jan 20, 2020

Update: I've now gotten this to run on real GAE, but now the testcases list page takes literally minutes to load.

@oliverchang
Copy link
Collaborator Author

oliverchang commented Jan 21, 2020

Went down a pretty deep rabbit hole to figure out why this testcases list (and only this page) was hanging with this new NDB.

In case anyone is interested, the root cause is that App Engine doesn't like it when a request creates threads which are still running at the end -- this will appear as if the request never completes, without any obvious indication why. This was also only reproducible on GAE, and not local dev_appserver.

The new NDB uses gRPC, which in turn uses google.auth.transports.grpc, which creates a ThreadPoolExecutor with 1 thread.

Somehow, when a MultiQuery happens (in the case of testcases list), things are set up such that there are references cycles which prevent the threads from getting stopped normally here.

So, at the end of every request, we explicitly call the GC, which fixes this.

There is also one more bug in NDB that I filed: googleapis/python-ndb#295

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

/gcbrun

2 similar comments
@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang oliverchang force-pushed the ndb branch 2 times, most recently from f0dbc8b to 88a3217 Compare January 24, 2020 06:53
@oliverchang
Copy link
Collaborator Author

Don't see any exceptions on the bots I staged over the weekend. This is ready for review while I do some more testing.

@oliverchang
Copy link
Collaborator Author

(CI will fail right now due to a conflicting dependency in the image, which is removed by this PR).

@oliverchang oliverchang changed the title (DON'T MERGE YET) Replace App Engine NDB with google cloud NDB. Replace App Engine NDB with google cloud NDB. Jan 28, 2020

next = __next__

def last_cursor(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: docstring

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

fuzzed_keys = StringProperty(indexed=False)
minimized_keys = StringProperty(indexed=False)
minidump_keys = StringProperty(indexed=False)
fuzzed_keys = TextProperty()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know how these are stored after we put the entities? I'm curious if these would be stringValues, blobValues, or something else.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are always stored as strings (assuming utf-8 encoding if byte are passed), per https://github.com/googleapis/python-ndb/blob/662a88a82691e69d282565b9d11706237da6f654/google/cloud/ndb/model.py#L2698

Also validated this by running the code.

Copy link
Collaborator

@inferno-chromium inferno-chromium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super awesome!, lets check this in to avoid b/148362873

@@ -131,11 +131,7 @@ def query_testcase(project_name, crash_type, crash_state, security_flag,
data_types.Testcase.open == is_open,
ndb_utils.is_false(data_types.Testcase.is_a_duplicate_flag)).order(
-data_types.Testcase.timestamp).iter(
limit=1,
projection=[
data_types.Testcase.bug_information,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really needed ? i prefer this style reference via doing typos in strings.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -53,12 +55,42 @@ def _combine(q1, q2):
return result


# TODO(ochang): Remove once bug is fixed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link to bug

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


return next(self._it)

next = __next__
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nite: move to start of class (not in middle of functions).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a common pattern (see e.g. https://github.com/googleapis/python-ndb/blob/4ffedc7b5a2366be15dcd299052d8a46a748addd/google/cloud/ndb/_datastore_query.py#L468). Putting it before won't work either becausess next won't be defined at that point. This is just to create an alias for python2/3 compatibility.

@@ -385,7 +385,7 @@ class Testcase(Model):
crash_type = StringProperty()

# Crashing address.
crash_address = StringProperty(indexed=False)
crash_address = TextProperty()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we lose values in old Testcase entities. Many times i have seen this if we change data type, it resets values in old entities ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. TextProperty is equivalent to StringProperty(indexed=False) (but the new NDB requires you to just use TextProperty instead).

@@ -631,6 +631,10 @@ def _pre_put_hook(self):
self.populate_indices()

def _post_put_hook(self, _):
if not self.key:
# Failed put.
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to log ? How is this expected, maybe expand comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was always wrong -- _post_put_hook can be called even if there was an exception in put(), and the exception will be rethrown automatically afterwards. Expanded the comment a bit more.

DO NOT MERGE YET.

Part of migration to Python 3. This is our last major blocker and
dependency on the Python 2 App Engine SDK.

Also remove ndb_patcher, as that is no longer required.
@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

/gcbrun

@oliverchang
Copy link
Collaborator Author

phew, CI is finally passing. Merging!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
cla: yes CLA signed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants