fix(sandbox): optimize tar preparation, file limit thresholds, and multi-cmd support
All checks were successful
build / build (push) Successful in 1m38s
build / build (pull_request) Successful in 1m36s
build / trigger-build-image (push) Has been skipped
build / trigger-build-image (pull_request) Has been skipped

This commit is contained in:
张泊明518370910136 2026-07-23 05:14:20 -07:00
parent 9b23765848
commit e751c9a237
GPG Key ID: D47306D7062CDA9D

View File

@ -10,13 +10,17 @@ import (
"maps" "maps"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/criyle/go-judge/pb" "github.com/criyle/go-judge/pb"
"github.com/joint-online-judge/JOJ3/internal/stage" "github.com/joint-online-judge/JOJ3/internal/stage"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
const tarThreshold = 128 * 1024 const (
tarSizeThreshold = 128 * 1024 // 128 KB
tarCountThreshold = 100 // 100 files
)
func (e *Sandbox) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) { func (e *Sandbox) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
var err error var err error
@ -28,13 +32,12 @@ func (e *Sandbox) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
} }
} }
for i := 0; i < len(cmds); i += 1 { for i := 0; i < len(cmds); i += 1 {
cmd := &cmds[i] if cmd := &cmds[i]; cmd.CopyIn == nil {
if cmd.CopyIn == nil {
cmd.CopyIn = make(map[string]stage.CmdFile) cmd.CopyIn = make(map[string]stage.CmdFile)
} }
for k, v := range cmd.CopyInCached { for k, v := range cmds[i].CopyInCached {
if fileID, ok := e.cachedMap[v]; ok { if fileID, ok := e.cachedMap[v]; ok {
cmd.CopyIn[k] = stage.CmdFile{FileID: &fileID} cmds[i].CopyIn[k] = stage.CmdFile{FileID: &fileID}
} }
} }
} }
@ -49,8 +52,7 @@ func prepareTar(cmds []stage.Cmd) (bool, []byte) {
return false, nil return false, nil
} }
for i := range cmds { for i := range cmds {
if cmds[i].CopyInDir != "" && if shouldTar(&cmds[i]) {
estimateCopyInSize(&cmds[i]) >= tarThreshold {
tarData, keysInTar := createCopyInTar(&cmds[i]) tarData, keysInTar := createCopyInTar(&cmds[i])
if tarData == nil { if tarData == nil {
return false, nil return false, nil
@ -68,6 +70,11 @@ func prepareTar(cmds []stage.Cmd) (bool, []byte) {
return false, nil return false, nil
} }
func shouldTar(cmd *stage.Cmd) bool {
size, count := estimateCopyIn(cmd)
return size >= tarSizeThreshold || count >= tarCountThreshold
}
func (e *Sandbox) runUnary(cmds []stage.Cmd) ([]stage.ExecutorResult, error) { func (e *Sandbox) runUnary(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
pbCmds := convertPBCmd(cmds) pbCmds := convertPBCmd(cmds)
for i, pbCmd := range pbCmds { for i, pbCmd := range pbCmds {
@ -100,35 +107,38 @@ func (e *Sandbox) runWithTar(cmds []stage.Cmd, tarData []byte) ([]stage.Executor
fid := fileIDResp.GetFileID() fid := fileIDResp.GetFileID()
slog.Debug("sandbox tar uploaded", "fileID", fid, "tarSize", len(tarData)) slog.Debug("sandbox tar uploaded", "fileID", fid, "tarSize", len(tarData))
defer func() {
deleteReq := &pb.FileID{}
deleteReq.SetFileID(fid)
if _, err := e.execClient.FileDelete(context.TODO(), deleteReq); err != nil {
slog.Warn("sandbox tar file delete", "fileID", fid, "error", err)
}
}()
tarFileName := "/w/__joj3_copyin.tar" tarFileName := "/w/__joj3_copyin.tar"
script := fmt.Sprintf( script := fmt.Sprintf(
"/bin/tar xf %s -C / --no-same-owner && rm %s && exec \"$@\"", "/bin/tar xf %s -C / --no-same-owner && rm -f %s && exec \"$@\"",
tarFileName, tarFileName, tarFileName, tarFileName,
) )
cmds[0].CopyIn[tarFileName] = stage.CmdFile{FileID: &fid} for i := range cmds {
cmds[0].Args = append([]string{ if cmds[i].CopyIn == nil {
"/bin/sh", "-c", script, "--", cmds[i].CopyIn = make(map[string]stage.CmdFile)
}, cmds[0].Args...) }
cmds[i].CopyIn[tarFileName] = stage.CmdFile{FileID: &fid}
cmds[i].Args = append([]string{
"/bin/sh", "-c", script, "_",
}, cmds[i].Args...)
}
slog.Debug("sandbox tar exec", "cmd", cmds[0].Args[:3]) slog.Debug("sandbox tar exec", "cmd", cmds[0].Args[:3])
results, err := e.runUnary(cmds) return e.runUnary(cmds)
if err != nil {
return nil, err
}
deleteReq := &pb.FileID{}
deleteReq.SetFileID(fid)
if _, err := e.execClient.FileDelete(context.TODO(), deleteReq); err != nil {
slog.Warn("sandbox tar file delete", "fileID", fid, "error", err)
}
return results, nil
} }
func estimateCopyInSize(cmd *stage.Cmd) int { func estimateCopyIn(cmd *stage.Cmd) (int, int) {
total := 0 totalSize := 0
totalCount := 0
if cmd.CopyInDir != "" { if cmd.CopyInDir != "" {
_ = filepath.Walk(cmd.CopyInDir, _ = filepath.Walk(cmd.CopyInDir,
func(path string, info os.FileInfo, err error) error { func(path string, info os.FileInfo, err error) error {
@ -140,7 +150,8 @@ func estimateCopyInSize(cmd *stage.Cmd) int {
return nil return nil
} }
if _, exists := cmd.CopyIn[relPath]; !exists { if _, exists := cmd.CopyIn[relPath]; !exists {
total += int(info.Size()) totalSize += int(info.Size())
totalCount++
} }
return nil return nil
}) })
@ -151,13 +162,22 @@ func estimateCopyInSize(cmd *stage.Cmd) int {
} }
if f.Src != nil { if f.Src != nil {
if fi, err := os.Stat(*f.Src); err == nil { if fi, err := os.Stat(*f.Src); err == nil {
total += int(fi.Size()) totalSize += int(fi.Size())
totalCount++
} }
} else if f.Content != nil { } else if f.Content != nil {
total += len(*f.Content) totalSize += len(*f.Content)
totalCount++
} }
} }
return total return totalSize, totalCount
}
func formatTarPath(p string) string {
if filepath.IsAbs(p) {
return strings.TrimPrefix(p, "/")
}
return "w/" + p
} }
func createCopyInTar(cmd *stage.Cmd) ([]byte, []string) { func createCopyInTar(cmd *stage.Cmd) ([]byte, []string) {
@ -193,20 +213,14 @@ func createCopyInTar(cmd *stage.Cmd) ([]byte, []string) {
if err != nil { if err != nil {
return err return err
} }
hdr.Name = relPath hdr.Name = formatTarPath(relPath)
if !filepath.IsAbs(hdr.Name) {
hdr.Name = "w/" + hdr.Name
}
return tw.WriteHeader(hdr) return tw.WriteHeader(hdr)
} }
hdr, err := tar.FileInfoHeader(info, "") hdr, err := tar.FileInfoHeader(info, "")
if err != nil { if err != nil {
return err return err
} }
hdr.Name = relPath hdr.Name = formatTarPath(relPath)
if !filepath.IsAbs(hdr.Name) {
hdr.Name = "w/" + hdr.Name
}
if info.IsDir() { if info.IsDir() {
hdr.Name += "/" hdr.Name += "/"
} }
@ -235,12 +249,8 @@ func createCopyInTar(cmd *stage.Cmd) ([]byte, []string) {
continue continue
} }
if f.Content != nil { if f.Content != nil {
name := k
if !filepath.IsAbs(name) {
name = "w/" + name
}
hdr := &tar.Header{ hdr := &tar.Header{
Name: name, Name: formatTarPath(k),
Mode: 0o644, Mode: 0o644,
Size: int64(len(*f.Content)), Size: int64(len(*f.Content)),
} }
@ -267,10 +277,7 @@ func createCopyInTar(cmd *stage.Cmd) ([]byte, []string) {
slog.Error("create copyIn tar file info header", "key", k, "error", err) slog.Error("create copyIn tar file info header", "key", k, "error", err)
continue continue
} }
hdr.Name = k hdr.Name = formatTarPath(k)
if !filepath.IsAbs(hdr.Name) {
hdr.Name = "w/" + hdr.Name
}
if err := tw.WriteHeader(hdr); err != nil { if err := tw.WriteHeader(hdr); err != nil {
slog.Error("create copyIn tar write header", "key", k, "error", err) slog.Error("create copyIn tar write header", "key", k, "error", err)
continue continue