Skip to content

datastore: add EnableKeyConversion for compatibility with Cloud Datastore encoded keys #192

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

Merged
merged 35 commits into from
May 14, 2019

Conversation

goog-lukemc
Copy link
Contributor

The main purpose of this change is to remove toil in migrating to the go111 runtime. This PR adds forward compatibility with cloud.google.com/go/datastore/key.Encode() so that customers can address migration to the go111 runtime in any order without concern for key encoding strings difference produced by the key.Encode() functions.

@goog-lukemc goog-lukemc changed the title Adds forward compatibility for with cloud.google.com/go/datastore/key.Encode() strings Adds forward compatibility with cloud.google.com/go/datastore/key.Encode() strings Feb 12, 2019
@sbuss sbuss requested a review from broady February 15, 2019 17:53
@Deleplace
Copy link
Contributor

CI fails for Go 1.8, as the new dependencies use type aliasing (and other things) added in 1.9.

This looks like another point in favor of more specific code duplication (though I ack the risk of future divergence).

@googlebot googlebot added cla: no and removed cla: yes labels May 9, 2019
@broady broady changed the title Adds forward compatibility with cloud.google.com/go/datastore/key.Encode() strings datastore: add EnableKeyConversion for compatibility with Cloud Datastore encoded keys May 9, 2019
@broady broady added the cla: yes label May 9, 2019
@googlebot googlebot removed the cla: no label May 9, 2019
@googlebot googlebot added cla: no and removed cla: yes labels May 9, 2019
@golang golang deleted a comment from googlebot May 9, 2019
@golang golang deleted a comment from googlebot May 9, 2019
@golang golang deleted a comment from googlebot May 9, 2019
@golang golang deleted a comment from googlebot May 9, 2019
@golang golang deleted a comment from googlebot May 9, 2019
@golang golang deleted a comment from googlebot May 9, 2019
@googlebot
Copy link
Collaborator

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of all the commit author(s), set the cla label to yes (if enabled on your project), and then merge this pull request when appropriate.

ℹ️ Googlers: Go here for more info.

1 similar comment
@googlebot
Copy link
Collaborator

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of all the commit author(s), set the cla label to yes (if enabled on your project), and then merge this pull request when appropriate.

ℹ️ Googlers: Go here for more info.

Copy link

@tbpg tbpg left a comment

Choose a reason for hiding this comment

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

Looks great overall!

func protoToKey(p *cloudpb.Key) (*Key, error) {
var key *Key
var namespace string
if partition := p.PartitionId; partition != nil {
Copy link

Choose a reason for hiding this comment

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

Could this be simplified to namespace := p.GetPartitionId().GetNamespaceId()? Normally, the Get function for protos handles nil values nicely.

Copy link
Contributor

Choose a reason for hiding this comment

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

Everything under internal/cloudkey and internal/cloudpb is copied/pasted from cloud.google.com/go/datastore, we should keep as-is.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make that more clear? I wasn't sure where the copy/paste started & stopped.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hopefully better now. Added package-level doc, too.

"github.com/golang/protobuf/proto"
)

// Code below is copied from google.golang.org/genproto/googleapis/datastore/v1
Copy link

Choose a reason for hiding this comment

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

Maybe comment on why it was copied?

Copy link
Contributor

Choose a reason for hiding this comment

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

PTAL at package-level doc.

@@ -254,6 +254,9 @@ func DecodeKey(encoded string) (*Key, error) {

ref := new(pb.Reference)
if err := proto.Unmarshal(b, ref); err != nil {
if k := decodeCloudKey(encoded); k != nil {
Copy link

Choose a reason for hiding this comment

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

Might be worth a comment explaining why this is done this way.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

keyConversion.mu.RLock()
project := keyConversion.project
keyConversion.mu.RUnlock()
return project
Copy link

Choose a reason for hiding this comment

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

Seems weird to R lock the mutex and return the value... Sounds like it would be easy to have a race condition when the value is used.

Copy link
Contributor

Choose a reason for hiding this comment

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

Once set, it's immutable and will always return the same value. Will add a comment.

return project
}

// decodeCloudKey attempts to decode the given encoded key generated by the Cloud Datastore client library, returning nil if the key couldn't be decoded.
Copy link

Choose a reason for hiding this comment

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

Reference the full package name rather than "Cloud Datastore client library"?

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

}

// decodeCloudKey attempts to decode the given encoded key generated by the Cloud Datastore client library, returning nil if the key couldn't be decoded.
func decodeCloudKey(encoded string) *Key {
Copy link

Choose a reason for hiding this comment

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

This should probably return an error. Good not to rely on special return values to indicate success/failure (other than an error).

Copy link
Contributor

Choose a reason for hiding this comment

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

This was a simplification - we don't use the error value. I think the contract of "non-nil if valid" is fine?

Copy link

Choose a reason for hiding this comment

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

SGTM. Similar to how protos work in general, I suppose.

"testing"
)

var testCasesKeyCompat = []struct {
Copy link

Choose a reason for hiding this comment

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

Not a big deal, but I usually prefer putting the test cases directly in the test where they're used. Then the slice can just be named tests because it's clear what they're for.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

t.Fatalf("%v", err.Error())
}
if !reflect.DeepEqual(dk, tc.key) {
t.Errorf("%s: got %+v, want %+v", tc.desc, dk, tc.key)
Copy link

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like tc.desc is logged. What would make it clearer?

Copy link

Choose a reason for hiding this comment

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

I prefer both (function name + desc), but OK with me to only do tc.desc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

func protoToKey(p *cloudpb.Key) (*Key, error) {
var key *Key
var namespace string
if partition := p.PartitionId; partition != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make that more clear? I wasn't sure where the copy/paste started & stopped.

// The context provided must be an App Engine context if running in first generation App Engine.
// This can be called in the /_ah/start handler. It is safe to call multiple times, and is cheap to call, so can also be inserted as middleware.
//
// Enabling key compatibility not affect the encoding format used by Key.Encode, it only expands the type of keys that are able to be decoded with DecodeKey.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: grammar. Should be "does not affect"?

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

//
// Enabling key compatibility not affect the encoding format used by Key.Encode, it only expands the type of keys that are able to be decoded with DecodeKey.
func EnableKeyConversion(ctx context.Context) {
if getKeyConversionAppID() == "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Am I misreading this? Won't getKeyConversionAppID() always return "" until the code below this runs? And so this effectively prevents keyConversion.project from ever getting set?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops. Expression was inverted. PTAL. Good catch.

@googlebot
Copy link
Collaborator

A Googler has manually verified that the CLAs look good.

(Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.)

ℹ️ Googlers: Go here for more info.

@googlebot googlebot removed the cla: no label May 14, 2019
@googlebot
Copy link
Collaborator

A Googler has manually verified that the CLAs look good.

(Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.)

ℹ️ Googlers: Go here for more info.

@broady broady merged commit 4c25cac into golang:master May 14, 2019
gopherbot pushed a commit to googleapis/google-cloud-go that referenced this pull request Jun 6, 2019
If a key can't be decoded, try to decode it with the encoding scheme from
google.golang.org/appengine/datastore.

Along with golang/appengine#192, this allows for
cross-compatibility for encoded keys across the two libraries.

Co-authored-by: Luke Mccoy <[email protected]>
Co-authored-by: Chris Broadfoot <[email protected]>

Change-Id: I7e091b37b184a12c30998c3c22baffb964018a61
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/41370
Reviewed-by: kokoro <[email protected]>
Reviewed-by: Tyler Bui-Palsulich <[email protected]>
Reviewed-by: Jean de Klerk <[email protected]>
Reviewed-by: Steven Buss <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants