perf: several dragged items cross to another place as one tar stream; older agents get them one by one

This commit is contained in:
redtea committed 2026-10-15 22:28:46 +03:00
1 parent 6916af4236
commit 7d1996dd7d
8 files changed
+301 -72

No files matched your search

+72 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"net/http"
"path"
"strconv"
"strings"
"sync"
@@ -119,6 +120,8 @@ type shipReq struct {
// file pane (its connection id); To is still the agent holding ToConn.
FromConn string `json:"from_conn"`
ToConn string `json:"to_conn"`
// More: other things of Src's folder sent along, under their own names.
More []string `json:"more"`
}
// POST /api/panel/agents/{id}/fs/ship - start sending; the answer names the
@@ -214,7 +217,27 @@ func oldAgent(res proto.TaskResult) proto.TaskResult {
}
func (s *Server) ship1(ctx context.Context, j *shipJob, from string, q shipReq) proto.TaskResult {
args := proto.FSShipArgs{Src: q.Src, Name: q.Name, Dir: q.Dir, Conflict: q.Conflict, Job: j.id}
if len(q.More) > 0 && !s.shipsSeveral(from, q) {
// One at a time: an SSH address, or an agent from before 0.2.0.
var last proto.TaskResult
done, skipped := 0, 0
for i, p := range append([]string{q.Src}, q.More...) {
one := q
one.Src, one.More = p, nil
if i > 0 {
one.Name = path.Base(path.Clean(p))
}
if last = s.ship1(ctx, j, from, one); !last.OK {
return last
}
var d struct{ Done, Skipped int }
_ = json.Unmarshal(last.Data, &d)
done, skipped = done+d.Done, skipped+d.Skipped
}
data, _ := json.Marshal(map[string]int{"done": done, "skipped": skipped})
return proto.TaskResult{OK: true, Data: data}
}
args := proto.FSShipArgs{Src: q.Src, Name: q.Name, Dir: q.Dir, Conflict: q.Conflict, Job: j.id, More: q.More}
if q.SSH != nil {
args.SSH, args.Move = q.SSH, q.Move
res, err := s.waitTask(ctx, from, proto.TaskFSShipOut, args, xferTimeout)
@@ -267,17 +290,19 @@ func (s *Server) ship1(ctx context.Context, j *shipJob, from string, q shipReq)
return out
}
if q.Move {
task, del := proto.TaskFSDelTree, any(proto.FSPathArgs{Path: q.Src})
if q.FromConn != "" {
raw, _ := json.Marshal(proto.FSPathArgs{Path: q.Src})
task, del = proto.TaskSSHFSOp, proto.SSHFSArgs{Conn: q.FromConn, Op: "deltree", Args: raw}
}
res, err := s.waitTask(ctx, from, task, del, fsTaskTimeout)
if err == nil && !res.OK {
err = errText(res.Error)
}
if err != nil {
return proto.TaskResult{Error: "отправлено, но здесь не удалилось: " + err.Error()}
for _, p := range append([]string{q.Src}, q.More...) {
task, del := proto.TaskFSDelTree, any(proto.FSPathArgs{Path: p})
if q.FromConn != "" {
raw, _ := json.Marshal(proto.FSPathArgs{Path: p})
task, del = proto.TaskSSHFSOp, proto.SSHFSArgs{Conn: q.FromConn, Op: "deltree", Args: raw}
}
res, err := s.waitTask(ctx, from, task, del, fsTaskTimeout)
if err == nil && !res.OK {
err = errText(res.Error)
}
if err != nil {
return proto.TaskResult{Error: "отправлено, но здесь не удалилось: " + err.Error()}
}
}
}
// The receiver's count: what it put down and what it kept as it was.
@@ -434,3 +459,38 @@ func (s *Server) handleRelayGet(w http.ResponseWriter, r *http.Request) {
}
_, _ = w.Write(data)
}
// shipsSeveral: both agents take several things in one stream (0.2.0 on).
func (s *Server) shipsSeveral(from string, q shipReq) bool {
if q.SSH != nil {
return false
}
for _, id := range []string{from, q.To} {
a, _ := s.db.GetAgent(id)
if a == nil || !versionAtLeast(a.Version, "0.2.0") {
return false
}
}
return true
}
// versionAtLeast: version v (dotted numbers) is min or newer; anything else
// is taken for older.
func versionAtLeast(v, min string) bool {
a, b := strings.Split(v, "."), strings.Split(min, ".")
for i := range b {
var x, y int
if i < len(a) {
n, err := strconv.Atoi(a[i])
if err != nil {
return false
}
x = n
}
y, _ = strconv.Atoi(b[i])
if x != y {
return x > y
}
}
return true
}