From 56120920ddb1e72c6a41e533ba603b85f2421673 Mon Sep 17 00:00:00 2001 From: Dominik Menke Date: Wed, 30 Oct 2024 20:58:16 +0100 Subject: [PATCH] refactor: use ULID as Request ID and re-use as workdir name Using the same ID allows associating the request with its (generated) files more easily, although this is only useful with --keep-jobs=always or --keep-jobs=on-failure. To decrease the chance of ID collisions, we're switching from manually generating 48 bits in base64 encoding to ULID [1], which boast a 48 bit timestamp with ms precision and 80 bits of randomness. [1]: https://github.com/oklog/ulid --- go.mod | 1 + go.sum | 3 +++ service/middleware/requestid.go | 8 ++++---- service/renderer.go | 3 +++ tex/document.go | 26 ++++++++++++++++++++++++-- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c7b6a55..f361a22 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/gorilla/handlers v1.5.2 github.com/gorilla/mux v1.8.1 github.com/moby/term v0.5.0 + github.com/oklog/ulid/v2 v2.1.0 github.com/opencontainers/image-spec v1.1.0 github.com/prometheus/client_golang v1.20.5 github.com/spf13/afero v1.11.0 diff --git a/go.sum b/go.sum index 144cf01..80a113e 100644 --- a/go.sum +++ b/go.sum @@ -1476,6 +1476,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= +github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -1490,6 +1492,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= diff --git a/service/middleware/requestid.go b/service/middleware/requestid.go index 19b1ced..c6548e5 100644 --- a/service/middleware/requestid.go +++ b/service/middleware/requestid.go @@ -3,10 +3,10 @@ package middleware import ( "context" "crypto/rand" - "encoding/base64" "fmt" "net/http" + "github.com/oklog/ulid/v2" "go.uber.org/zap" ) @@ -17,11 +17,11 @@ type contextKey string const ContextKey = contextKey("request-id") func generateRequestId() string { - b := make([]byte, 6) - if _, err := rand.Read(b); err != nil { + id, err := ulid.New(ulid.Now(), rand.Reader) + if err != nil { panic(fmt.Errorf("rand error: %w", err)) } - return base64.RawURLEncoding.EncodeToString(b) + return id.String() } func RequestID(next http.Handler) http.Handler { diff --git a/service/renderer.go b/service/renderer.go index d93fa50..4f7920f 100644 --- a/service/renderer.go +++ b/service/renderer.go @@ -65,6 +65,9 @@ func (svc *service) render(log *zap.Logger, res http.ResponseWriter, req *http.R defer svc.release() doc := tex.NewDocument(log, engine, image) + if id, ok := middleware.GetRequestID(req); ok { + doc.SetWorkingDirName(id) + } defer func() { if svc.shouldKeepJobs(err) { return diff --git a/tex/document.go b/tex/document.go index aa8bab4..87943e6 100644 --- a/tex/document.go +++ b/tex/document.go @@ -5,6 +5,7 @@ import ( "io" "os" "path" + "path/filepath" "strings" "sync" @@ -101,6 +102,14 @@ type Document interface { // named directory. WorkingDirectory() (string, error) + // SetWorkingDirName disables the randomness of WorkingDirectory() by + // providing a static name. Extra care should be taken to ensure + // concurrent renderings don't accidentally happen in the same directory. + // + // Calling this method after WorkingDirectory(), AddFile(), or + // NewWriter() doesn't have an effect. + SetWorkingDirName(name string) + // AddFile saves the given content as a file in the document's // working directory, with the given name. // @@ -179,8 +188,9 @@ type document struct { image string engine Engine - mkWorkDir *sync.Once - mkWorkDirErr error + mkWorkDirName string + mkWorkDir *sync.Once + mkWorkDirErr error } var _ Document = (*document)(nil) @@ -205,6 +215,11 @@ func (doc *document) WorkingDirectory() (string, error) { } func (doc *document) createWorkDir() { + if doc.mkWorkDirName != "" { + doc.workdir = filepath.Join(baseJobDir, doc.mkWorkDirName) + return + } + if wd, err := afero.TempDir(doc.fs, baseJobDir, "texd-"); err != nil { doc.mkWorkDirErr = UnknownError("creating working directory failed", err, nil) } else { @@ -212,6 +227,13 @@ func (doc *document) createWorkDir() { } } +func (doc *document) SetWorkingDirName(name string) { + if doc.workdir != "" { + return // createWorkDir was already called + } + doc.mkWorkDirName = name +} + func (doc *document) AddFile(name, contents string) error { wc, err := doc.NewWriter(name) if err != nil {