Can I use Goroutines in Google App Engine (Standard Environment)? - google-app-engine

The following example seems to work, but is it safe to use? My goal is to do some very light background processing (whereas an actual task queue job feels too heavy).
func MyHandler(w http.ResponseWriter, r *http.Request) {
go func() {
// do something ...
}()
return // 200
}

Goroutines that outlive the request are not supported, but you can use runtime.RunInBackground to execute code in a background goroutine:
func MyHandler(w http.ResponseWriter, r *http.Request) {
err := runtime.RunInBackground(c, func(c appengine.Context) {
// do something...
})
return // 200
}
The provided function will be invoked with a background context that is distinct from (and may outlast) the provided context. Note that there is a limit of 10 simultaneous background requests per instance. Here is another example.
Please note that Goroutines that live within the context of a request, are supported though:
The Go runtime environment for App Engine provides full support for
goroutines, but not for parallel execution: goroutines are scheduled
onto a single operating system thread. This single-thread restriction
may be lifted in future versions. Multiple requests may be handled
concurrently by a given instance; that means that if one request is,
say, waiting for a datastore API call, another request may be
processed by the same instance. (Source)

Related

How to build a async rest endpoint that calls blocking action in worker thread and replies instantly (Quarkus)

I checked the docs and stackoverflow but didn't find exactly a suiting approach.
E.g. this post seems very close: Dispatch a blocking service in a Reactive REST GET endpoint with Quarkus/Mutiny
However, I don't want so much unneccessary boilerplate code in my service, at best, no service code change at all.
I generally just want to call a service method which uses entity manager and thus is a blocking action, however, want to return a string to the caller immidiately like "query started" or something. I don't need a callback object, it's just a fire and forget approach.
I tried something like this
#NonBlocking
#POST
#Produces(MediaType.TEXT_PLAIN)
#Path("/query")
public Uni<String> triggerQuery() {
return Uni.createFrom()
.item("query started")
.call(() -> service.startLongRunningQuery());
}
But it's not working -> Error message returned to the caller:
You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread.",
I actually expected quarkus takes care to distribute the tasks accordingly, that is, rest call to io thread and blocking entity manager operations to worker thread.
So I must using it wrong.
UPDATE:
Also tried an proposed workaround that I found in https://github.com/quarkusio/quarkus/issues/11535 changing the method body to
return Uni.createFrom()
.item("query started")
.emitOn(Infrastructure.getDefaultWorkerPool())
.invoke(()-> service.startLongRunningQuery());
Now I don't get an error, but service.startLongRunningQuery() is not invoked, thus no logs and no query is actually sent to db.
Same with (How to call long running blocking void returning method with Mutiny reactive programming?):
return Uni.createFrom()
.item(() ->service.startLongRunningQuery())
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool())
Same with (How to run blocking codes on another thread and make http request return immediately):
ExecutorService executor = Executors.newFixedThreadPool(10, r -> new Thread(r, "CUSTOM_THREAD"));
return Uni.createFrom()
.item(() -> service.startLongRunningQuery())
.runSubscriptionOn(executor);
Any idea why service.startLongRunningQuery() is not called at all and how to achieve fire and forget behaviour, assuming rest call handled via IO thread and service call handled by worker thread?
It depends if you want to return immediately (before your startLongRunningQuery operation is effectively executed), or if you want to wait until the operation completes.
If the first case, use something like:
#Inject EventBus bus;
#NonBlocking
#POST
#Produces(MediaType.TEXT_PLAIN)
#Path("/query")
public void triggerQuery() {
bus.send("some-address", "my payload");
}
#Blocking // Will be called on a worker thread
#ConsumeEvent("some-address")
public void executeQuery(String payload) {
service.startLongRunningQuery();
}
In the second case, you need to execute the query on a worker thread.
#POST
#Produces(MediaType.TEXT_PLAIN)
#Path("/query")
public Uni<String> triggerQuery() {
return Uni.createFrom(() -> service.startLongRunningQuery())
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
}
Note that you need RESTEasy Reactive for this to work (and not classic RESTEasy). If you use classic RESTEasy, you would need the quarkus-resteasy-mutiny extension (but I would recommend using RESTEasy Reactive, it will be way more efficient).
Use the EventBus for that https://quarkus.io/guides/reactive-event-bus
Send and forget is the way to go.

How do I add custom tracing to a second-generation App Engine application in Go?

Google App Engine now supports Go 1.11 via the new second-generation standard environment. While converting an older standard environment application to the second generation, it wasn't obvious how to combine tracing information from the app engine infrastructure with custom tracing I added to the application using OpenCensus.
Even though I had created a stackdriver exporter and registered traces, I wasn't seeing custom trace information in the stackdriver console attached to inbound requests.
The key is I was missing is understanding how span context is communicated to the serving app. Google leverages the X-Cloud-Trace-Context header to propagate span context within requests sent to your serving instances, and the go.opencensus.io/exporter/stackdriver/propagation library provides an implementation to extract and persist this information within http requests.
Don't forget to create a stackdriver exporter, and register traces to it. The docs for the exporter library show an example of this.
// CreateSpanFromRequest returns a context and span based on the http.Request.
// If no existing spancontext is found, this will start a new span.
// Modifies existing request to contain the generated span's context.
func CreateSpanFromRequest(name string, r *http.Request) (context.Context, *trace.Span) {
var span *trace.Span
ctx := r.Context()
httpFormat := &propagation.HTTPFormat{}
sc, ok := httpFormat.SpanContextFromRequest(r)
if ok {
ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc)
} else {
ctx, span = trace.StartSpan(ctx, name)
}
// Write the span context into the http.Request. We do this to
// to enable chaining handlers together more easily.
httpFormat.SpanContextToRequest(span.SpanContext(), r)
return ctx, span
}
Using this, I was able to add custom spans to my handlers that would be properly associated with the incoming request information in stackdriver:
func indexHandler(w http.ResponseWriter, r *http.Request) {
_, span := CreateSpanFromRequest("indexHandler", r)
defer span.End()
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
fmt.Fprint(w, "Hello, World!")
}

How to add custom span into Trace, in go

My app runs on App Engine Standard and the Go runtime.
I have this trace for my recent request:
There is a big gap between the "urlfetch" span and the "datastore_v3" span, because my app processes some CPU-bound computation for ~1000ms.
I would love to programmatically add my computation as a custom span into the Trace view, and get something like this:
Is there a way to do this in my app written in go? (source here)
It appears it might be possible. From Setting Up Stackdriver Trace for Go:
Alpha
This is an alpha release of the OpenCensus package for Go. These
libraries might be changed in backward-incompatible ways and are not
recommended for production use. They are not subject to any SLA or
deprecation policy.
Stackdriver Trace can be used by Go applications using the
OpenCensus package for Go.
Stackdriver Trace's Go support is provided by OpenCensus, a set
of tracing and application metrics instrumentation libraries that work
with multiple backends. The latest details about OpenCensus for Go,
along with additional documentation and examples, can be found on its
GitHub page.
Support is enabled by default in the flexible environment, however the docs make no mention about the standard environment (if that's your case I'd say just give it a try). From App Engine:
On Google App Engine flexible environment, the Stackdriver Trace API
access scope is enabled by default, and the OpenCensus client
library can be used without needing to provide credentials or a
project ID.
An application code sample is provided on the same page.
I could make it work with the new AppEngine runtime for Go 1.11 (currently in beta) and OpenCensus with the Stackdriver exporter.
In order to attach my custom span to the main Trace of the request, I use this utility func:
// Start a new span "With Remote Parent"
func startSpanfWRT(r *http.Request, msg string, args ...interface{}) (c2 context.Context, endSpan func()) {
caption := fmt.Sprintf(msg, args...)
c := r.Context()
spanContext, ok := (&propagation.HTTPFormat{}).SpanContextFromRequest(r)
if !ok {
return c, func() {}
}
var span *trace.Span
c2, span = trace.StartSpanWithRemoteParent(c, caption, spanContext)
endSpan = func() {
span.End()
}
return c2, endSpan
}
Note that it requires the *http.Request as argument (a context.Context wouldn't be enough here).
Here is the sample app source code.
As a span needs to be started and then later stopped, the start func returns an "end" callback, and a new Context as well.
It is fine to call startSpanfWRT multiple times, and they may overlap. It requires to pass the *http.Request around, which is not super-convenient (usually we only pass Contexts around).
However, after a call to startSpanfWRT, you may add children spans conveniently, just paying attention to the respective Contexts:
c2, childSpan := trace.StartSpan(c, caption)

Go App Engine get version in init() without Context

Is there a way to to get my autoscaled application's VersionID in my init() function without a Context? The only available option seems to be appengine.VersionID(context.Context). Manually scaled instances have /_ah/start called when they start up (giving access to a Context), but there is nothing like this for autoscaled instances.
I am not caring about the generated ID that appengine.VersionID returns with it, just the app.yaml version.
EDIT: A bit of context: I am wanting to deploy versions in the form x-x-x-dev or x-x-x-live and have my database connection depend on the version suffix. This way, when I look in the GCP console, I can be certain which deployed modules/services are using which database. Of course, I setup my DB connection pool in the init(), which has no access to a Context.
I searched and searched with no answers online anywhere, so here it is.
Simply parse the app.yaml file in your init() function. My example here uses a yaml parsing package, but it can be done more lightweight if you need.
import "github.com/ghodss/yaml"
type AppVersion struct {
Version string `json:"version"`
}
func VersionID() (string, error) {
dat, err := ioutil.ReadFile("app.yaml")
if err != nil {
return "", err
}
a := &AppVersion{}
err = yaml.Unmarshal(dat, a)
if err != nil {
return "", err
}
return a.Version, nil
}
Note that this DOES NOT return the generated ID in the form X.Y that appengine.VersionID() does. Only the X part of the version.
As an aside, in the appengine repo on Github, the actual call to appengine.VersionID requires a Context, but internally calls the internal package with nil. So they basically force you to call it with a Context, but it isn't actually used. It's incredibly infuriating.
EDIT: It should be noted that the new Go SDK in gcloud no longer supports version in the app.yaml, as it is now a required parameter at deploy. However, the "legacy" SDK is still supported and maintained, which I am continuing to use as of today (12/24/2018).

Is it possible to recover from panic on google app engine?

I'm wondering if it's possible to recover from a panic. It seems GAE has it's own panic recovery mechanism but I can't find any hook to handle it on my app.
Handlers in an AppEngine webapp are registered in the same way as would be in a normal Go application. You just don't have to call http.ListenAndServe() explicitly (because it will be by the platform), and handler registration happens in an init() function (not in main()).
Having said that, the same panic-recover wrapping works on AppEngine too, and unfortunately there is no other, better way.
Take a look at this example: it uses a function registered with HandleFunc() and a Handler registered with Handle() to handle 2 URL patterns, but both intentionally panics (they refuse to serve):
func myHandleFunc(w http.ResponseWriter, r *http.Request) {
panic("I'm myHandlerFunc and I refuse to serve!")
}
type MyHandler int
func (m *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
panic("I'm MyHandler and I refuse to serve!")
}
func main() {
http.HandleFunc("/myfunc", myHandleFunc)
http.Handle("/myhandler", new(MyHandler))
panic(http.ListenAndServe(":8080", nil))
}
Directing your browser to http://localhost:8080/myfunc and http://localhost:8080/myhandler results in HTTP 500 status: internal server error (or an empty response depending on where you check it).
The general idea is to use recover to "catch" the panics from handlers (spec: Handling panics). We can "wrap" handle functions or Handlers in a way that we first register a defer statement which is called even if the rest of the function panics, and in which we recover from the panicing state.
See these 2 functions:
func protectFunc(hf func(http.ResponseWriter,
*http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
// hf() paniced, we just recovered from it.
// Handle error somehow, serve custom error page.
w.Write([]byte("Something went bad but I recovered and sent this!"))
}
}()
hf(w, r)
}
}
func protectHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
// h.ServeHTTP() paniced, we just recovered from it.
// Handle error somehow, serve custom error page.
w.Write([]byte("Something went bad but I recovered and sent this!"))
}
}()
h.ServeHTTP(w, r)
})
}
The first one takes a function and returns one which calles the one we passed but recovers from panicing state if one was initiated.
The second one takes a Handler and returns another Handler which similarly calls the passed one but also handles panics and restores normal execution.
Now if we register handler functions and Handlers protected by these methods, the registered handlers will never panic (assuming the code after restoring normal execution does not panic):
http.HandleFunc("/myfunc-protected", protectFunc(myHandleFunc))
http.Handle("/myhandler-protected", protectHandler(new(MyHandler)))
Visiting http://localhost:8080/myfunc-protected and http://localhost:8080/myhandler-protected URLs resuls in HTTP 200 status (OK) with the message:
Something went bad but I recovered and sent this!

Resources