-
Notifications
You must be signed in to change notification settings - Fork 1.6k
NDB: Sketch high level strategy #6908
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
Conversation
…n is just going to fix anyway.
ndb/src/google/cloud/ndb/_api.py
Outdated
state = _runstate.current() | ||
request = datastore_pb2.LookupRequest(project_id=state.project) | ||
key_pb = request.keys.add() | ||
key_pb.CopyFrom(key._key.to_protobuf()) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
ndb/src/google/cloud/ndb/_future.py
Outdated
|
||
"""A Future class.""" | ||
|
||
_NOT_COMPUTED = object() |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""A Future class.""" |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
ndb/src/google/cloud/ndb/_future.py
Outdated
|
||
def get_result(self): | ||
"""Get the computed result for this future.""" | ||
if self._result is _NOT_COMPUTED: |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This class is meant to be subclassed with code to compute a result from the | ||
results of the wrapped gRPC futures. :method:`_compute_result` should be | ||
overriden to compute a result from the completed gRPC calls. | ||
""" |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
|
||
def _compute_result(self, result): | ||
from google.cloud.ndb import model # avoid circular import | ||
return model._entity_from_protobuf(result.found[0].entity) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
class GetByKeyFuture(_future.Future): | ||
"""Future for looking up entities by Key.""" | ||
|
||
def _compute_result(self, result): |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
ndb/tests/system/test_system.py
Outdated
ds_key = ds_client.key("SomeKind", 1234) | ||
ds_entity = datastore.Entity(key=ds_key) | ||
ds_entity.update({"foo": 42, "bar": "none"}) | ||
ds_client.put(ds_entity) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
ndb/tests/system/test_system.py
Outdated
ds_client = datastore.Client() | ||
ds_key = ds_client.key("SomeKind", 1234) | ||
ds_entity = datastore.Entity(key=ds_key) | ||
ds_entity.update({"foo": 42, "bar": "none"}) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
""" | ||
_NOT_COMPUTED = object() | ||
_result = _NOT_COMPUTED | ||
_complete = False |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
|
||
|
||
def test_retrieve_entity(ds_entity): | ||
ds_entity("SomeKind", 1234, foo=42, bar="none") |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
bar = ndb.StringProperty() | ||
|
||
client = ndb.Client() | ||
with client.context(): |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
@dhermes This is good feedback, thanks! This code was meant to be "sketchy", so I could hammer out a basic idea without getting bogged down too far in details. In general, though, I'm just looking to use this to talk about high level approach. If that looks good, I'll start fleshing out the individual components of this "for real", with all relevant coding standards, etc... |
I think I've had a brainstorm for how I can do this in a way that integrates with the tasklet eventloop. This means, basically, starting over on this sketch, so I'm going to close this one and start up a new branch with the new idea. As difficult as it is to try and see all the moving parts at the same time, simply forging ahead without really taking tasklets into account is likely to paint us into a corner where implementing tasklets isn't really possible without another big rewrite. I think I can do better than that. |
👍 |
Diff base is #6888
This code is intentionally sketchy. It contains very partial implementations, non-conformant docstrings, and less than 100% test coverage. This code is not being proposed for merge but is offered as a sketch for discussion of general strategy and/or a guide for me to follow to produce a more complete, real implementation.
The main point of this was to provide the bare minimum to make the single system test pass. This test uses
google.cloud.datastore
to write an entity to Datastore and usesgoogle.cloud.ndb
to read it back out of Datastore as an instance of an NDB model class. I suggest looking at the test and thinking about if that is the correct user experience.The salient points are:
google.cloud.ndb.Client
as a parallel togoogle.cloud.datastore.Client
, as a means of bootstrapping NDB and its connection to Datastore in a way familiar to GCP users. This is the entry point for establishing credentials, the working project/app, and the namespace, exactly likegoogle.cloud.datastore
.Client.context()
as a context manager for encapsulating code that uses NDB. Under the hood there is a thread local state which legacy NDB previously managed in GAE by attaching it to the running HTTP request, an fixture which is no longer available in GCP. Use of the context manager means most existing projects which use NDB can simply wrap their code in a context and their existing business code continues to work, as it can access any needed state from the running context._future.Future
as a thin wrapper around Futures returned by gRPC. This class is meant to be subclassed and can wrap any number of gRPC Futures and compute an arbitrary result based on the results of those wrapped futures. As an example, there iskey.GetByKeyFuture
which wraps the return value of a call toLookup.future()
in Datastore's gRPC API and marsahall's an NDB entity from the returned Datastore protocol buffer. It's not doing any async on its own, here, just wrapping the raw gRPC async calls to provide Futures that yield arbitrarily computed results.I think pretty much everything except the
tasklets
surface can be implemented in this way. I haven't fully worked out, yet, how this will integrate with thetasklets
mini-framework but I think having a lot of the rest fleshed out will give us a good starting point.