Skip to content

Commit 0c87c9a

Browse files
committed
feat(containers): long running background task
1 parent 0a2c4f2 commit 0c87c9a

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM golang:1.23-alpine3.21 AS builder
2+
3+
WORKDIR /app
4+
5+
COPY go.* ./
6+
RUN go mod download
7+
8+
COPY . ./
9+
10+
RUN go build -o server ./cmd/server
11+
12+
FROM alpine:3.21
13+
14+
COPY --from=builder /app/server /app/server
15+
16+
CMD ["/app/server"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Long Running Task
2+
3+
This example showcases running a long background task in a container during the 15m retention period.
4+
5+
## Deploying
6+
7+
This example can be deployed using the Scaleway CLI:
8+
9+
```bash
10+
scw container deploy region=pl-waw
11+
```
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"log/slog"
7+
"net/http"
8+
"os"
9+
"os/signal"
10+
"sync"
11+
"syscall"
12+
"time"
13+
14+
"github.com/google/uuid"
15+
)
16+
17+
var (
18+
signalChan = make(chan os.Signal, 1)
19+
wg sync.WaitGroup
20+
21+
disableGracefulShutdown = os.Getenv("DISABLE_GRACEFUL_SHUTDOWN") != ""
22+
23+
longRunningJobDuration = os.Getenv("LONG_RUNNING_JOB_DURATION")
24+
parsedLongRunningJobDuration time.Duration
25+
defaultLongRunningJobDuration = 14 * time.Minute
26+
)
27+
28+
func main() {
29+
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
30+
AddSource: true,
31+
})))
32+
33+
if longRunningJobDuration != "" {
34+
var err error
35+
36+
parsedLongRunningJobDuration, err = time.ParseDuration(longRunningJobDuration)
37+
if err != nil {
38+
slog.Error("failed to parse LONG_RUNNING_JOB_DURATION", "error", err)
39+
os.Exit(1)
40+
}
41+
42+
slog.Info("LONG_RUNNING_JOB_DURATION set", "duration", parsedLongRunningJobDuration)
43+
} else {
44+
parsedLongRunningJobDuration = defaultLongRunningJobDuration
45+
slog.Info("LONG_RUNNING_JOB_DURATION not set, using default", "duration", parsedLongRunningJobDuration)
46+
}
47+
48+
srv := &http.Server{
49+
Addr: ":8080",
50+
Handler: http.HandlerFunc(handler),
51+
}
52+
53+
// Handle SIGTERM.
54+
if !disableGracefulShutdown {
55+
signal.Notify(signalChan, syscall.SIGTERM)
56+
}
57+
58+
// Start HTTP server.
59+
if disableGracefulShutdown {
60+
slog.Info("server started")
61+
62+
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
63+
slog.Error("server failed to start", "error", err)
64+
os.Exit(1)
65+
}
66+
} else {
67+
go func() {
68+
slog.Info("server started")
69+
70+
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
71+
slog.Error("server failed to start", "error", err)
72+
os.Exit(1)
73+
}
74+
}()
75+
76+
sig := <-signalChan
77+
slog.Info("received signal", "signal", sig)
78+
79+
// This should not take a lot of time because the handler is non-blocking.
80+
if err := srv.Shutdown(context.Background()); err != nil {
81+
slog.Error("server failed to shutdown", "error", err)
82+
}
83+
84+
// This is where we wait for the long running tasks to finish.
85+
wg.Wait()
86+
}
87+
}
88+
89+
func handler(w http.ResponseWriter, _ *http.Request) {
90+
wg.Add(1)
91+
92+
taskID := uuid.NewString()
93+
go longBackgroundTask(taskID, parsedLongRunningJobDuration)
94+
95+
w.WriteHeader(http.StatusOK)
96+
_, _ = w.Write([]byte("Started long background task: " + taskID))
97+
}
98+
99+
func longBackgroundTask(taskID string, duration time.Duration) {
100+
defer wg.Done()
101+
102+
slog.Info("long background task started", "taskID", taskID, "duration", duration)
103+
104+
sleepTime := 1 * time.Second
105+
numIterations := int(duration / sleepTime)
106+
107+
for i := range numIterations {
108+
slog.Info("long background task running...",
109+
"iteration", i,
110+
"total", numIterations,
111+
"taskID", taskID)
112+
113+
time.Sleep(sleepTime)
114+
}
115+
116+
slog.Info("long background task finished", "taskID", taskID)
117+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module github.com/scaleway/serverless-examples/containers/long-running-task-go
2+
3+
go 1.23
4+
5+
toolchain go1.23.4
6+
7+
require github.com/google/uuid v1.6.0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

0 commit comments

Comments
 (0)