-
Notifications
You must be signed in to change notification settings - Fork 171
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support Unpickling CachedQueryResults Created By Py2 (#3234)
This is a rabbit hole, as pickling across python versions is a terrible idea. However, we can hack around a few of the major issues that come up. Start by adding a repro unit test that tries to load+depickle a value I got from the datastore admin tool for something written by the py2 app. Next, we need to fix: - we changed the import path of models, so we'll need to rewrite those on the fly. We can do this with a custom `Unpickler`. See [this answer](https://stackoverflow.com/a/40916570). - bytestrings are handled totally differently between python 2 and 3, so we need to make sure that the py3 reader will unpickle using `encoding="bytes"`. We can do this in our custom unpickler too. Plus there's this whole thing about unpickling `datetimes`. See [this answer](https://stackoverflow.com/a/28218598). - Finally, there's a ndb incompatibility. The legacy app uses a [custom pickling format](https://github.com/GoogleCloudPlatform/datastore-ndb-python/blob/master/ndb/model.py#L2964-L2970) (a serialized protobuf), which it does by defining `__getstate__` and `__setstate` on the base `Model` class. Since the py3 compatible ndb library does not do this, unpickling them will fail. I filed googleapis/python-ndb#587 in hope of working with upstream to fix this, but if not, we can potentially hack around it ourselves by manually deserializing the protobuf and setting the field ourselves. **WARNING**: After this, we should be able to read data written by the legacy app, but we can not yet write data that the legacy app can read. So after this, when we re-enable `CachedQueryResult`, we'll have to make sure that we don't write anything to the datastore.
- Loading branch information
1 parent
4c04276
commit 10c7a61
Showing
20 changed files
with
988 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
326 changes: 326 additions & 0 deletions
326
src/backend/common/queries/tests/cached_query_result_compatibility_test.py
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from google.cloud.datastore.batch import Batch as Batch | ||
from google.cloud.datastore.client import Client as Client | ||
from google.cloud.datastore.entity import Entity as Entity | ||
from google.cloud.datastore.key import Key as Key | ||
from google.cloud.datastore.query import Query as Query | ||
from google.cloud.datastore.transaction import Transaction as Transaction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from google.protobuf import descriptor_pb2 as descriptor_pb2 | ||
from typing import Any | ||
|
||
DESCRIPTOR: Any | ||
Reference: Any | ||
Path: Any |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from google.cloud._helpers import make_secure_channel as make_secure_channel | ||
from google.cloud._http import DEFAULT_USER_AGENT as DEFAULT_USER_AGENT | ||
from google.cloud.datastore_v1.gapic import datastore_client as datastore_client | ||
from typing import Any | ||
|
||
def make_datastore_api(client: Any): ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from google.cloud import exceptions as exceptions | ||
from google.rpc import status_pb2 as status_pb2 | ||
from typing import Any, Optional | ||
|
||
DATASTORE_API_HOST: str | ||
API_BASE_URL: Any | ||
API_VERSION: str | ||
API_URL_TEMPLATE: str | ||
|
||
def build_api_url(project: Any, method: Any, base_url: Any): ... | ||
|
||
class HTTPDatastoreAPI: | ||
client: Any = ... | ||
def __init__(self, client: Any) -> None: ... | ||
def lookup(self, project_id: Any, keys: Any, read_options: Optional[Any] = ...): ... | ||
def run_query(self, project_id: Any, partition_id: Any, read_options: Optional[Any] = ..., query: Optional[Any] = ..., gql_query: Optional[Any] = ...): ... | ||
def begin_transaction(self, project_id: Any, transaction_options: Optional[Any] = ...): ... | ||
def commit(self, project_id: Any, mode: Any, mutations: Any, transaction: Optional[Any] = ...): ... | ||
def rollback(self, project_id: Any, transaction: Any): ... | ||
def allocate_ids(self, project_id: Any, keys: Any): ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from google.cloud.datastore import helpers as helpers | ||
from typing import Any, Optional | ||
|
||
class Batch: | ||
def __init__(self, client: Any) -> None: ... | ||
def current(self): ... | ||
@property | ||
def project(self): ... | ||
@property | ||
def namespace(self): ... | ||
@property | ||
def mutations(self): ... | ||
def put(self, entity: Any) -> None: ... | ||
def delete(self, key: Any) -> None: ... | ||
def begin(self) -> None: ... | ||
def commit(self, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... | ||
def rollback(self) -> None: ... | ||
def __enter__(self): ... | ||
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from google.api_core.gapic_v1 import client_info as client_info | ||
from google.auth.credentials import AnonymousCredentials as AnonymousCredentials | ||
from google.cloud.client import ClientWithProject as ClientWithProject | ||
from google.cloud.datastore import helpers as helpers | ||
from google.cloud.datastore._gapic import make_datastore_api as make_datastore_api | ||
from google.cloud.datastore._http import HTTPDatastoreAPI as HTTPDatastoreAPI | ||
from google.cloud.datastore.batch import Batch as Batch | ||
from google.cloud.datastore.entity import Entity as Entity | ||
from google.cloud.datastore.key import Key as Key | ||
from google.cloud.datastore.query import Query as Query | ||
from google.cloud.datastore.transaction import Transaction as Transaction | ||
from typing import Any, Optional | ||
|
||
DATASTORE_EMULATOR_HOST: str | ||
DATASTORE_DATASET: str | ||
DISABLE_GRPC: str | ||
|
||
class Client(ClientWithProject): | ||
SCOPE: Any = ... | ||
namespace: Any = ... | ||
def __init__(self, project: Optional[Any] = ..., namespace: Optional[Any] = ..., credentials: Optional[Any] = ..., client_info: Any = ..., client_options: Optional[Any] = ..., _http: Optional[Any] = ..., _use_grpc: Optional[Any] = ...) -> None: ... | ||
@property | ||
def base_url(self): ... | ||
@base_url.setter | ||
def base_url(self, value: Any) -> None: ... | ||
@property | ||
def current_batch(self): ... | ||
@property | ||
def current_transaction(self): ... | ||
def get(self, key: Any, missing: Optional[Any] = ..., deferred: Optional[Any] = ..., transaction: Optional[Any] = ..., eventual: bool = ..., retry: Optional[Any] = ..., timeout: Optional[Any] = ...): ... | ||
def get_multi(self, keys: Any, missing: Optional[Any] = ..., deferred: Optional[Any] = ..., transaction: Optional[Any] = ..., eventual: bool = ..., retry: Optional[Any] = ..., timeout: Optional[Any] = ...): ... | ||
def put(self, entity: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... | ||
def put_multi(self, entities: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... | ||
def delete(self, key: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... | ||
def delete_multi(self, keys: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... | ||
def allocate_ids(self, incomplete_key: Any, num_ids: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...): ... | ||
def key(self, *path_args: Any, **kwargs: Any): ... | ||
def batch(self): ... | ||
def transaction(self, **kwargs: Any): ... | ||
def query(self, **kwargs: Any): ... | ||
def reserve_ids_sequential(self, complete_key: Any, num_ids: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... | ||
def reserve_ids(self, complete_key: Any, num_ids: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...): ... | ||
def reserve_ids_multi(self, complete_keys: Any, retry: Optional[Any] = ..., timeout: Optional[Any] = ...) -> None: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from typing import Any, Optional | ||
|
||
class Entity(dict): | ||
key: Any = ... | ||
exclude_from_indexes: Any = ... | ||
def __init__(self, key: Optional[Any] = ..., exclude_from_indexes: Any = ...) -> None: ... | ||
def __eq__(self, other: Any) -> Any: ... | ||
def __ne__(self, other: Any) -> Any: ... | ||
@property | ||
def kind(self): ... | ||
@property | ||
def id(self): ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from google.cloud.datastore.entity import Entity as Entity | ||
from google.cloud.datastore.key import Key as Key | ||
from google.cloud.datastore_v1.proto import datastore_pb2 as datastore_pb2, entity_pb2 as entity_pb2 | ||
from google.protobuf import struct_pb2 as struct_pb2 | ||
from google.type import latlng_pb2 as latlng_pb2 | ||
from typing import Any | ||
|
||
def entity_from_protobuf(pb: Any): ... | ||
def entity_to_protobuf(entity: Any): ... | ||
def get_read_options(eventual: Any, transaction_id: Any): ... | ||
def key_from_protobuf(pb: Any): ... | ||
|
||
class GeoPoint: | ||
latitude: Any = ... | ||
longitude: Any = ... | ||
def __init__(self, latitude: Any, longitude: Any) -> None: ... | ||
def to_protobuf(self): ... | ||
def __eq__(self, other: Any) -> Any: ... | ||
def __ne__(self, other: Any) -> Any: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from typing import Any, Optional | ||
|
||
class Key: | ||
def __init__(self, *path_args: Any, **kwargs: Any) -> None: ... | ||
def __eq__(self, other: Any) -> Any: ... | ||
def __ne__(self, other: Any) -> Any: ... | ||
def __hash__(self) -> Any: ... | ||
def completed_key(self, id_or_name: Any): ... | ||
def to_protobuf(self): ... | ||
def to_legacy_urlsafe(self, location_prefix: Optional[Any] = ...): ... | ||
@classmethod | ||
def from_legacy_urlsafe(cls, urlsafe: Any): ... | ||
@property | ||
def is_partial(self): ... | ||
@property | ||
def namespace(self): ... | ||
@property | ||
def path(self): ... | ||
@property | ||
def flat_path(self): ... | ||
@property | ||
def kind(self): ... | ||
@property | ||
def id(self): ... | ||
@property | ||
def name(self): ... | ||
@property | ||
def id_or_name(self): ... | ||
@property | ||
def project(self): ... | ||
@property | ||
def parent(self): ... |
Oops, something went wrong.