Datastore: create parent and child in object group transaction?

After getting familiar with Google Datastore concepts / theories, I started using Go to Data Store

Scenario: Kinds User

and LinkedAccount

requires each user to have one or more linked accounts (yay third party login). For strong consistency, LinkedAccounts will be children of the linked user. Creating a new user then involves creating both a user and a LinkedAccount, not just one.

User creation seems like an ideal option for transactions. If, say, the creation of a LinkedAccount fails, the transaction is rolled back with an error. This is not currently possible. The goal is to create a parent and then a child in a transaction.

According to the docs

All Datastore operations in a transaction must operate on objects in the same group of objects if the transaction is a single group transaction.

We want new User

and LinkedAccount

were in the same group, so it seems to me that the Datastore must support this scenario. I'm afraid the intended implication is that operations on existing objects in the same group can be done in a single transaction.

tx, err := datastore.NewTransaction(ctx)
if err != nil {
    return err
}
incompleteUserKey := datastore.NewIncompleteKey(ctx, "User", nil)
pendingKey, err := tx.Put(incompleteUserKey, user)
if err != nil {
    return err
}
incompleteLinkedAccountKey := datastore.NewIncompleteKey(ctx, "GithubAccount", incompleteUserKey)
// also tried PendingKey as parent, but its a separate struct type
_, err = tx.Put(incompleteLinkedAccountKey, linkedAccount)
if err != nil {
    return err
}
// attempt to commit
if _, err := tx.Commit(); err != nil {
    return err
}
return nil

      

It's clear from the source of the library why this doesn't work. PendingKey

are not keys, and incomplete keys cannot be used as parents.

Is this a necessary limitation of the data store or library? For those facing this type of requirement, were you just sacrificing strong consistency and making both kinds of global?

For Google:

  • datastore: invalid key
  • datastore: cannot use pendingKey as type * "google.golang.org/cloud/datastore".Key
+3


source to share


1 answer


It should be noted that transactions in the Cloud Datastore API can run up to 25 entity groups , but this does not answer the question of how to create two entities in the same entity group as part of the same transaction.

There are several ways to approach this (note that this applies to any use of the cloud storage API, not just the library gcloud-golang

):



  • Use the (string) name for the parent key instead of having Datastore automatically assign a numeric ID:

    parentKey := datastore.NewKey(ctx, "Parent", "parent-name", 0, nil)
    childKey := datastore.NewIncompleteKey(ctx, "Child", parentKey)
    
          

  • Make an explicit call AllocateIds

    to have the datastore pick a numeric identifier for the parent key:

    incompleteKeys := [1]*datastore.Key{datastore.NewIncompleteKey(ctx, "Parent", nil)}
    completeKeys, err := datastore.AllocateIDs(ctx, incompleteKeys)
    if err != nil {
      // ...
    }
    parentKey := completeKeys[0]
    childKey := datastore.NewIncompleteKey(ctx, "Child", parentKey)
    
          

+3


source







All Articles