mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-24 06:50:40 +02:00
fix: clone protobuf snapshots without copying internal locks (#11968)
* fix: clone protobuf snapshots without copying internal locks * fix: reuse cloned snapshot fields when filtering
This commit is contained in:
1 parent
f08f075d23
commit
00e64441c6
11 files changed
+156
-43
No files matched your search
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
@@ -614,7 +615,7 @@ func deriveSchedulerAdminRuntime(
|
||||
descriptor *plugin_pb.JobTypeDescriptor,
|
||||
) *plugin_pb.AdminRuntimeConfig {
|
||||
if cfg != nil && cfg.AdminRuntime != nil {
|
||||
adminConfig := *cfg.AdminRuntime
|
||||
adminConfig := proto.Clone(cfg.AdminRuntime).(*plugin_pb.AdminRuntimeConfig)
|
||||
// Overlay descriptor defaults for any zero numeric fields. Persisted
|
||||
// configs from older versions have no execution_timeout_seconds, and
|
||||
// without this overlay the scheduler would fall back to the 90s
|
||||
@@ -646,7 +647,7 @@ func deriveSchedulerAdminRuntime(
|
||||
adminConfig.ExecutionTimeoutSeconds = defaults.ExecutionTimeoutSeconds
|
||||
}
|
||||
}
|
||||
return &adminConfig
|
||||
return adminConfig
|
||||
}
|
||||
|
||||
if descriptor == nil || descriptor.AdminRuntimeDefaults == nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const defaultWorkerStaleTimeout = 2 * time.Minute
|
||||
@@ -495,36 +496,35 @@ func cloneJobTypeCapability(in *plugin_pb.JobTypeCapability) *plugin_pb.JobTypeC
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := *in
|
||||
return &out
|
||||
return proto.Clone(in).(*plugin_pb.JobTypeCapability)
|
||||
}
|
||||
|
||||
func cloneWorkerHeartbeat(in *plugin_pb.WorkerHeartbeat) *plugin_pb.WorkerHeartbeat {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := *in
|
||||
out := proto.Clone(in).(*plugin_pb.WorkerHeartbeat)
|
||||
if in.RunningWork != nil {
|
||||
out.RunningWork = make([]*plugin_pb.RunningWork, 0, len(in.RunningWork))
|
||||
for _, rw := range in.RunningWork {
|
||||
if rw == nil {
|
||||
continue
|
||||
running := out.RunningWork[:0]
|
||||
// Clone can turn nil messages into empty messages, so filter using
|
||||
// their original positions while reusing the cloned entries.
|
||||
for i, work := range in.RunningWork {
|
||||
if work != nil {
|
||||
running = append(running, out.RunningWork[i])
|
||||
}
|
||||
clone := *rw
|
||||
out.RunningWork = append(out.RunningWork, &clone)
|
||||
}
|
||||
}
|
||||
if in.QueuedJobsByType != nil {
|
||||
out.QueuedJobsByType = make(map[string]int32, len(in.QueuedJobsByType))
|
||||
for k, v := range in.QueuedJobsByType {
|
||||
out.QueuedJobsByType[k] = v
|
||||
clear(out.RunningWork[len(running):])
|
||||
if running == nil {
|
||||
running = []*plugin_pb.RunningWork{}
|
||||
}
|
||||
out.RunningWork = running
|
||||
}
|
||||
if in.Metadata != nil {
|
||||
out.Metadata = make(map[string]string, len(in.Metadata))
|
||||
for k, v := range in.Metadata {
|
||||
out.Metadata[k] = v
|
||||
}
|
||||
// Preserve writable empty maps without rebuilding populated clones.
|
||||
if in.QueuedJobsByType != nil && out.QueuedJobsByType == nil {
|
||||
out.QueuedJobsByType = make(map[string]int32)
|
||||
}
|
||||
return &out
|
||||
if in.Metadata != nil && out.Metadata == nil {
|
||||
out.Metadata = make(map[string]string)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
|
||||
)
|
||||
|
||||
func TestCloneWorkerHeartbeatFiltersNilAndIsolatesState(t *testing.T) {
|
||||
t.Parallel()
|
||||
source := &plugin_pb.WorkerHeartbeat{
|
||||
RunningWork: []*plugin_pb.RunningWork{nil, {WorkId: "first"}, nil, {WorkId: "second"}, nil},
|
||||
QueuedJobsByType: map[string]int32{"vacuum": 2},
|
||||
Metadata: map[string]string{"rack": "rack-a"},
|
||||
}
|
||||
got := cloneWorkerHeartbeat(source)
|
||||
if len(got.RunningWork) != 2 || got.RunningWork[0].GetWorkId() != "first" || got.RunningWork[1].GetWorkId() != "second" {
|
||||
t.Fatalf("unexpected filtered work: %v", got.RunningWork)
|
||||
}
|
||||
got.RunningWork[0].WorkId = "changed"
|
||||
got.QueuedJobsByType["vacuum"] = 99
|
||||
got.Metadata["rack"] = "changed"
|
||||
if len(source.RunningWork) != 5 || source.RunningWork[0] != nil || source.RunningWork[1].WorkId != "first" {
|
||||
t.Fatalf("cloning mutated source work: %v", source.RunningWork)
|
||||
}
|
||||
if source.QueuedJobsByType["vacuum"] != 2 || source.Metadata["rack"] != "rack-a" {
|
||||
t.Fatal("cloned maps alias the source")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneWorkerHeartbeatPreservesEmptyCollections(t *testing.T) {
|
||||
t.Parallel()
|
||||
if cloneWorkerHeartbeat(nil) != nil {
|
||||
t.Fatal("nil heartbeat must stay nil")
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
source *plugin_pb.WorkerHeartbeat
|
||||
wantNilWork bool
|
||||
}{
|
||||
{"nil", &plugin_pb.WorkerHeartbeat{}, true},
|
||||
{"empty", &plugin_pb.WorkerHeartbeat{
|
||||
RunningWork: []*plugin_pb.RunningWork{},
|
||||
QueuedJobsByType: map[string]int32{},
|
||||
Metadata: map[string]string{},
|
||||
}, false},
|
||||
{"all nil work", &plugin_pb.WorkerHeartbeat{RunningWork: []*plugin_pb.RunningWork{nil, nil}}, false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := cloneWorkerHeartbeat(tc.source)
|
||||
if len(got.RunningWork) != 0 || (got.RunningWork == nil) != tc.wantNilWork {
|
||||
t.Fatalf("unexpected empty work slice: %#v", got.RunningWork)
|
||||
}
|
||||
if (got.QueuedJobsByType == nil) != (tc.source.QueuedJobsByType == nil) || (got.Metadata == nil) != (tc.source.Metadata == nil) {
|
||||
t.Fatal("clone changed whether empty maps are writable")
|
||||
}
|
||||
if got.Metadata != nil {
|
||||
got.Metadata["added"] = "value"
|
||||
if len(tc.source.Metadata) != 0 {
|
||||
t.Fatal("empty metadata map aliases source")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user