Reputation: 547
I am playing with the google cloud environment for the first time specially with the google app engine and the datastore, everything works fine when I run it locally. I am authenticating with the datastore by setting the environment variable GOOGLE_APPLICATION_CREDENTIALS as per the documentation. But once I deploy to app engine the requests always time out, it seems as if the GetAll method never returns. Below is the code for my application:
package app
import (
"fmt"
"net/http"
"time"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/cloud/datastore"
)
type User struct {
FirstName string
LastName string
Email string
Created time.Time
id int64
}
func init() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
var err error
var dbClient *datastore.Client
var ctx context.Context
ctx = appengine.NewContext(r)
dbClient, err = datastore.NewClient(ctx, "app-id")//this has the real app id, not sure if this is meant to be secret
if err != nil {
fmt.Fprintf(w, "Could not create datastore client: %+v", err)
return
}
defer dbClient.Close()
var users []*User
query := datastore.NewQuery("User").Filter("Email=", "[email protected]")
keys, err := dbClient.GetAll(ctx, query, &users)
if err != nil {
fmt.Fprintf(w, "Could not query users: %+v", err)
return
}
for i, key := range keys {
users[i].id = key.ID()
fmt.Fprintf(w, "%+v\n",users[i])
}
fmt.Fprintf(w, "done!!!")
}
The errors in the App Engine logs have the following two lines:
This request caused a new process to be started for your application, and thus caused your application code to be loaded for the first time. This request may thus take longer and use more CPU than a typical request for your application.
Process terminated because the request deadline was exceeded. (Error code 123)
Btw, this finishes lightning fast on my local and there is only one record in my datastore. Any guess as to why this could be happening or how to debug it? Thanks
Upvotes: 0
Views: 989
Reputation: 547
The solution for my problem was to switch the datastore library
from: "google.golang.org/cloud/datastore"
to: "google.golang.org/appengine/datastore"
appengine/datastore seems to use a different protocol and makes it so that you do not have to use the GOOGLE_APPLICATION_CREDENTIALS environment variable to authenticate. It will use the dev server for the different app engine services when used locally and the cloud resources when deployed.
Upvotes: 1
Reputation: 5178
The datastore
packages uses gRPC to connect to the datastore service. You can't use this directly on App Engine, since you can't make TCP connections directly.
You'll need to use the sockets
API to make the TCP connection for you:
import "google.golang.org/appengine/socket" // et al
ctx := appengine.NewContext(r)
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
dialer := func(addr string, timeout time.Duration) (net.Conn, error) {
return socket.DialTimeout(ctx, "tcp", addr, timeout)
}
client, err := datastore.NewClient(ctx, "app-id", cloud.WithGRPCDialOption(grpc.WithDialer(dialer)))
You can also invoke dialer
directly when debugging to make sure it is able to reach datastore.googleapis.com:443 as expected:
conn, err := dialer("datastore.googleapis.com:443", 5*time.Second)
if err != nil {
log.Errorf(ctx, "Dial: %v", err)
http.Error(w, "Dial failed: "+err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Addr: %v\n", conn.RemoteAddr())
conn.Close()
Upvotes: 1