diff options
Diffstat (limited to 'modules/actions/log.go')
-rw-r--r-- | modules/actions/log.go | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/modules/actions/log.go b/modules/actions/log.go index c38082b5dc..5a1425e031 100644 --- a/modules/actions/log.go +++ b/modules/actions/log.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/dbfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/zstd" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" "google.golang.org/protobuf/types/known/timestamppb" @@ -28,6 +29,9 @@ const ( defaultBufSize = MaxLineSize ) +// WriteLogs appends logs to DBFS file for temporary storage. +// It doesn't respect the file format in the filename like ".zst", since it's difficult to reopen a closed compressed file and append new content. +// Why doesn't it store logs in object storage directly? Because it's not efficient to append content to object storage. func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runnerv1.LogRow) ([]int, error) { flag := os.O_WRONLY if offset == 0 { @@ -106,6 +110,17 @@ func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limi return rows, nil } +const ( + // logZstdBlockSize is the block size for zstd compression. + // 128KB leads the compression ratio to be close to the regular zstd compression. + // And it means each read from the underlying object storage will be at least 128KB*(compression ratio). + // The compression ratio is about 30% for text files, so the actual read size is about 38KB, which should be acceptable. + logZstdBlockSize = 128 * 1024 // 128KB +) + +// TransferLogs transfers logs from DBFS to object storage. +// It happens when the file is complete and no more logs will be appended. +// It respects the file format in the filename like ".zst", and compresses the content if needed. func TransferLogs(ctx context.Context, filename string) (func(), error) { name := DBFSPrefix + filename remove := func() { @@ -119,7 +134,26 @@ func TransferLogs(ctx context.Context, filename string) (func(), error) { } defer f.Close() - if _, err := storage.Actions.Save(filename, f, -1); err != nil { + var reader io.Reader = f + if strings.HasSuffix(filename, ".zst") { + r, w := io.Pipe() + reader = r + zstdWriter, err := zstd.NewSeekableWriter(w, logZstdBlockSize) + if err != nil { + return nil, fmt.Errorf("zstd NewSeekableWriter: %w", err) + } + go func() { + defer func() { + _ = w.CloseWithError(zstdWriter.Close()) + }() + if _, err := io.Copy(zstdWriter, f); err != nil { + _ = w.CloseWithError(err) + return + } + }() + } + + if _, err := storage.Actions.Save(filename, reader, -1); err != nil { return nil, fmt.Errorf("storage save %q: %w", filename, err) } return remove, nil @@ -150,11 +184,22 @@ func OpenLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeek } return f, nil } + f, err := storage.Actions.Open(filename) if err != nil { return nil, fmt.Errorf("storage open %q: %w", filename, err) } - return f, nil + + var reader io.ReadSeekCloser = f + if strings.HasSuffix(filename, ".zst") { + r, err := zstd.NewSeekableReader(f) + if err != nil { + return nil, fmt.Errorf("zstd NewSeekableReader: %w", err) + } + reader = r + } + + return reader, nil } func FormatLog(timestamp time.Time, content string) string { |