forked from cadence-workflow/cadence
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexecutor.go
More file actions
132 lines (115 loc) · 4.65 KB
/
Copy pathexecutor.go
File metadata and controls
132 lines (115 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package handler
import (
"context"
"errors"
"fmt"
"time"
"github.com/uber/cadence/common/clock"
"github.com/uber/cadence/common/log"
"github.com/uber/cadence/common/log/tag"
"github.com/uber/cadence/common/types"
"github.com/uber/cadence/service/sharddistributor/config"
"github.com/uber/cadence/service/sharddistributor/store"
)
const (
_heartbeatRefreshRate = 2 * time.Second
)
type executor struct {
logger log.Logger
timeSource clock.TimeSource
storage store.Store
shardDistributionCfg config.ShardDistribution
}
func NewExecutorHandler(
logger log.Logger,
storage store.Store,
timeSource clock.TimeSource,
shardDistributionCfg config.ShardDistribution,
) Executor {
return &executor{
logger: logger,
timeSource: timeSource,
storage: storage,
shardDistributionCfg: shardDistributionCfg,
}
}
func (h *executor) Heartbeat(ctx context.Context, request *types.ExecutorHeartbeatRequest) (*types.ExecutorHeartbeatResponse, error) {
previousHeartbeat, assignedShards, err := h.storage.GetHeartbeat(ctx, request.Namespace, request.ExecutorID)
// We ignore Executor not found errors, since it just means that this executor heartbeat the first time.
if err != nil && !errors.Is(err, store.ErrExecutorNotFound) {
return nil, fmt.Errorf("get heartbeat: %w", err)
}
now := h.timeSource.Now().UTC()
mode := h.shardDistributionCfg.GetMigrationMode(request.Namespace)
switch mode {
case types.MigrationModeINVALID:
h.logger.Warn("Migration mode is invalid", tag.ShardNamespace(request.Namespace), tag.ShardExecutor(request.ExecutorID))
return nil, fmt.Errorf("migration mode is invalid")
case types.MigrationModeLOCALPASSTHROUGH:
h.logger.Warn("Migration mode is local passthrough, no calls to heartbeat allowed", tag.ShardNamespace(request.Namespace), tag.ShardExecutor(request.ExecutorID))
return nil, fmt.Errorf("migration mode is local passthrough")
// From SD perspective the behaviour is the same
case types.MigrationModeLOCALPASSTHROUGHSHADOW, types.MigrationModeDISTRIBUTEDPASSTHROUGH:
assignedShards, err = h.assignShardsInCurrentHeartbeat(ctx, request, previousHeartbeat, assignedShards)
if err != nil {
return nil, err
}
}
// If the state has changed we need to update heartbeat data.
// Otherwise, we want to do it with controlled frequency - at most every _heartbeatRefreshRate.
if previousHeartbeat != nil && request.Status == previousHeartbeat.Status && mode == types.MigrationModeONBOARDED {
lastHeartbeatTime := time.Unix(previousHeartbeat.LastHeartbeat, 0)
if now.Sub(lastHeartbeatTime) < _heartbeatRefreshRate {
return _convertResponse(assignedShards, mode), nil
}
}
newHeartbeat := store.HeartbeatState{
LastHeartbeat: now.Unix(),
Status: request.Status,
ReportedShards: request.ShardStatusReports,
}
err = h.storage.RecordHeartbeat(ctx, request.Namespace, request.ExecutorID, newHeartbeat)
if err != nil {
return nil, fmt.Errorf("record heartbeat: %w", err)
}
return _convertResponse(assignedShards, mode), nil
}
// assignShardsInCurrentHeartbeat is used during the migration phase to assign the shards to the executors according to what is reported during the heartbeat
func (h *executor) assignShardsInCurrentHeartbeat(ctx context.Context, request *types.ExecutorHeartbeatRequest, previousHeartbeat *store.HeartbeatState, previousAssignedShards *store.AssignedState) (*store.AssignedState, error) {
assignedShards := *previousAssignedShards
assignedShards = store.AssignedState{
AssignedShards: make(map[string]*types.ShardAssignment),
LastUpdated: h.timeSource.Now().Unix(),
ModRevision: int64(0),
}
err := h.storage.DeleteExecutors(ctx, request.GetNamespace(), []string{request.GetExecutorID()}, store.NopGuard())
if err != nil {
return nil, fmt.Errorf("delete executors: %w", err)
}
for shard := range request.GetShardStatusReports() {
assignedShards.AssignedShards[shard] = &types.ShardAssignment{
Status: types.AssignmentStatusREADY,
}
}
assignShardsRequest := store.AssignShardsRequest{
NewState: &store.NamespaceState{
ShardAssignments: map[string]store.AssignedState{
request.GetExecutorID(): assignedShards,
},
},
}
err = h.storage.AssignShards(ctx, request.GetNamespace(), assignShardsRequest, store.NopGuard())
if err != nil {
return nil, fmt.Errorf("assign shards in current heartbeat: %w", err)
}
return &assignedShards, nil
}
func _convertResponse(shards *store.AssignedState, mode types.MigrationMode) *types.ExecutorHeartbeatResponse {
res := &types.ExecutorHeartbeatResponse{}
if shards == nil {
return res
}
res.ShardAssignments = shards.AssignedShards
res.MigrationMode = mode
return res
}