Interested in using gorilla/mux. I'm quite new to asynch APIs in general too. So say I just want to implement a simple http server in Go. I would like to just store the status of these asynch API job IDs in-memory for now. So when a request comes in, the point of an asynch API would be for a long running process which the caller of the API could poll periodically until it finishes right. So when the request comes in, is the idea to create a goroutine (I am new to goroutines) for the long running task, and then immediately return a job ID back to the caller so that they can poll for the status of the job? How would you do this?
#At a high level, how would you implement an asynch API in Go?
27 messages · Page 1 of 1 (latest)
the only thing you commonly need is a locked map, which saves the job's info and status.
this can be easily done with standard package net/http, no need for third party frameworks.
first you need a map and a sync.Mutex to lock it (or just use sync.Map)
when a user invokes, create job status struct which also under a mutex (the implementation details is depends on what information you need), and then save the status in the map. You usually will have a context in the status for cancel the long running job later
in the goroutine, you just continues poll your job (also the context if you want to cancel it), and save the status to the struct you created. when the job is done, you can decides to remove the status struct from the map immediately, or keep it until a user gets the result.
hm, so something like this would be missing this idea of polling for this job. I guess the job here is simple enough, but we might want a separate goroutine to periodically poll the status of the job locally to update the task store
func asyncHandler(w http.ResponseWriter, r *http.Request) {
id := generateID()
mu.Lock()
taskStore[id] = TaskStatus{Status: "Processing"}
mu.Unlock()
go func(id string) {
// Simulate a long-running task
time.Sleep(5 * time.Second)
mu.Lock()
taskStore[id] = TaskStatus{Status: "Completed", Message: "Task completed successfully"}
mu.Unlock()
}(id)
response := map[string]string{"id": id}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
oh, the routine that is executing the job should be constantly checking the store too right. in case i cancel it explicitly
and i like the idea of keeping the result around until a user polls a successful
also what are you referring to in net/http ? Are you saying there is something in it that is useful for locking the map or?
I mean use net/http is enough so don't need gorilla/mux, but you still can try gorilla if you want
ah ok. The code base I am using is in gorilla/mux, so that's why I'm leaning on it.
but thanks, this is all very good to know
yes this code looks good, unless you are doing something more complex
I recommend don't assign taskStore[id] again, but change the TaskStatus directly by pointer (so you need a map to store *TaskStatus but not TaskStatus, and you also need a mutex in the TaskStatus)
Yea, I can describe what I am working on. I work in database infrastructure (I'm a jr). We have basically a local container orchestrator that runs on every host that starts up the database containers. It's a simple http server with start, stop, status, and what it does it call start container on docker and so on. Right now, those APIs are synchronous, but sometimes the database can take much longer to start up, so it would make sense to make a set of async APIs
If you are making dashboard, I'd recommend websocket for server side push
There's no dashboard or anything.
oh does the taskstore also need a mutex?
i guess i can just simplify all this with sync.map
I'll say that for start/stop containers there is no need for poll, because container can only be either start or stop, there is no 50% started state isn't it?
If you want show start log, that's another story and shouldn't included in status API
Oh, what is the reason that we use pointers here instead?
so you can change the value of the status rather than reassign it in the map
It's not a big deal in your case tho
Oh, yea, some additional context, a start may run multiple containers before starting the database container, and some containers are ran before the db container like one that downloads a backup from a remote location, as part of a broader restore procedure
so that's why we are looking into async
i see, that makes sense
yes if you have multiple stage, you will need change the status multiple times. however you don't need another goroutine for poll, just change the status between each action
yw