I am building a REST API in golang using Echo. I have a handler function Temp that handles a Post request with some form-data.
func NewBatchHandler(e *echo.Echo, us domain.BatchUsecase) {
handler := &BatchHandler{
BUsecase: us,
}
e.POST("/upload", handler.Temp)
}
func (a *BatchHandler) Temp(c echo.Context) error {
email, organization, file, err := validateFormInput(c)
if err != nil {
return c.JSON(http.StatusUnprocessableEntity, err.Error())
}
filePath := getFilePath(homeDir)
err = a.BUsecase.Save(file, homeDir, filePath)
if err != nil {
return c.JSON(getStatusCode(err), ResponseError{Message: err.Error()})
}
err = a.BUsecase.ValidateFile(filePath)
if err != nil {
return c.JSON(getStatusCode(err), ResponseError{Message: err.Error()})
}
ctx := c.Request().Context()
ca := make(chan []domain.Variant, file.Size)
go func() {
res, err := a.BUsecase.DoSomethingForEveryLong(ctx, filePath)
if err != nil {
logrus.Error("Error while annotating file: ", err)
return
}
ca <- res
}()
select {
case result := <-ca:
return c.String(http.StatusAccepted, fmt.Sprintf("Successfully Annotated the file", len(result)))
case <-c.Request().Context().Done(): // Check context.
// If it reaches here, this means that context was canceled (a timeout was reached, etc.).
return nil
}
}
I want the Temp function to return with a 202 statuscode after ValidateFile returns but I want to execute some long running functions and dont want the client to wait for them. How can I implement this?
func (a *batchUsecase) DoSomethingForEveryLong(ctx context.Context, filePath string) (res []domain.Variant, err error) {
ctx, cancel := context.WithTimeout(ctx, a.contextTimeout)
defer cancel()
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
variantStr := scanner.Text()
logrus.Info("Preparing query for variant: ", variantStr)
variant, err := a.batchRepo.Fetch(ctx, variantStr)
if err != nil {
return nil, fmt.Errorf("failed to fetch variant: %w", err)
}
res = append(res, variant)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("failed to scan file: %w", err)
}
logrus.Info("Annotated file successfully", res)
return res, nil
}
Here's a DoSomethingForVeryLong function for reference. Am I missing something very trivial?
So far I have tried looking into channels and goroutines
In the case that you don't need to include the result from the long running task in the response, you can do it like the demo at the bottom of this answer.
The demo dedicates a big part in the
mainfunc to make sure the process is not terminated immediately after the echo server is shutdown. So the long running tasks can run to the finish correctly.You don't have to write your code exactly the same as this one. This is just a demo to show the idea.