-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathexecutor.go
More file actions
253 lines (219 loc) · 7.32 KB
/
Copy pathexecutor.go
File metadata and controls
253 lines (219 loc) · 7.32 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
package main
import (
"fmt"
"log"
"os"
"runtime"
"strconv"
"sync"
"time"
)
// Executor executes the list of tasks
// The executor also waits for all the tasks to be completed
// Tasks is the number of tasks waiting to be executed by the executor
// wg is the wait group to wait all the task execution*/
// resultChan is the channel to post task result
// isRunnung marks if the the executor is currently running
// summary is the summary of execution
type Executor struct {
Tasks []*Task
wg sync.WaitGroup
resultChan chan Result
isRunning bool
summary Summary
// mutex *sync.Mutex
}
// Summary is the summary of execution
// successCount is the number of success task executed
// failedCount is the number of failed task executed
// startTime is the time of start of the execution
// maxTimeTakingFailedTask is the failed task result taking max time
type Summary struct {
successCount int
failedCount int
startTime int64
TaskResults []*Result
maxTimeTakingSuccessfulTask *Result
maxTimeTakingFailedTask *Result
}
// Result is the result for the task execution
// msg is the message from the result
// err is the error occured
// id the result id
// time is the time taken to run the task
type Result struct {
msg string
err error
id string
time int64
}
// Task is a operation to be performed
// evert task must implement these methods inorder to be executed
type Task interface {
init(resultChan chan Result)
onSetup()
onStart()
onEnd()
}
// init initialized executor params
func (executor *Executor) init() {
executor.Tasks = []*Task{}
executor.isRunning = false
executor.resultChan = make(chan Result)
executor.summary.successCount = 0
executor.summary.failedCount = 0
executor.summary.TaskResults = []*Result{}
// executor.mutex = &sync.Mutex{}
}
// addTask adds a new task to the executor
func (executor *Executor) addTask(task Task) {
task.init(executor.resultChan)
executor.Tasks = append(executor.Tasks, &task)
}
// execute will execute the added tasks
func (executor *Executor) execute() {
executor.summary.startTime = time.Now().UnixNano()
// start to listen the task resuls
go executor.listen()
executor.runBatch()
}
// runBatch will the tasks in batch
// the batch number will be set to environment as NUM_OF_BATCH
// the batch number is added from cli argument in main.go
func (executor *Executor) runBatch() {
tasksLen := len(executor.Tasks)
log.Printf("Task lens :: %d", tasksLen)
if tasksLen == 0 {
executor.isRunning = false
close(executor.resultChan)
// notifyAllTaskCompleted(executor)
// executor.generateSummary()
return
}
batchSize, _ := strconv.Atoi(os.Getenv("NUM_OF_BATCH"))
var to int
if tasksLen >= batchSize {
to = batchSize
} else {
to = tasksLen
}
// run all tasks
for i := 0; i < to; i++ {
executor.wg.Add(1)
executor.startTask(i)
}
executor.Tasks = executor.Tasks[to:tasksLen]
// wait for the task response
executor.wg.Wait()
// once one batch is compeleted run the next batch
executor.runBatch()
}
// startTask will start a specific task form the index value
func (executor *Executor) startTask(index int) {
task := executor.Tasks[index]
(*task).onSetup()
go (*task).onStart()
}
// listen will wait for all the result to be publishe in the result channel
// no matter its server or client the resulst should be pulbished in the resultChan
func (executor *Executor) listen() {
executor.isRunning = true
total := 0
from := 0
to := 0
// batch is the number of result to be sent in one summary
// we are not sending the result execution details to master at once instead we send the summary of certain number
// of task execution
// here we are sending summary when 500 tasks are completed
batch := 500
for executor.isRunning == true {
result := <-executor.resultChan
if result.id == "" {
// it marks that the channel has been closed
break
}
total++
executor.processResult(&result)
if mode != "MASTER" {
if (from + batch) <= len(executor.summary.TaskResults) {
to = from + batch
} else {
to = len(executor.summary.TaskResults)
}
executor.summary.TaskResults = append(executor.summary.TaskResults, &result)
// publishResult(&result)
// log.Printf("total task %d gr : %d cpu : %d", total, runtime.NumGoroutine(), runtime.NumCPU())
if len(executor.summary.TaskResults)%batch == 0 {
// log.Printf("posting summary from %d to %d", from, to)
go postSummaryInBatch(executor, from, to)
from = to
}
executor.wg.Done()
}
}
if mode != "MASTER" {
// flush the summary if necessary
postSummaryInBatch(executor, from, len(executor.summary.TaskResults))
}
log.Printf("stop listening now active go routinges %d", runtime.NumGoroutine())
}
func (executor *Executor) processResult(result *Result) {
// executor.mutex.Lock()
// defer executor.mutex.Unlock()
if result.err != nil {
//there was a error on task
executor.processFailedResult(result)
} else {
// the task executed successfully
executor.processSuccessResult(result)
}
// log.Printf("\ndone processing %s", result.id)
}
// handle the success result
func (executor *Executor) processSuccessResult(result *Result) {
// fmt.Printf("Success task id : %s , msg : %s , time : %d\n", result.id, result.msg, result.time)
executor.summary.successCount++
if executor.summary.maxTimeTakingSuccessfulTask == nil {
executor.summary.maxTimeTakingSuccessfulTask = result
return
}
if executor.summary.maxTimeTakingSuccessfulTask.time < result.time {
executor.summary.maxTimeTakingSuccessfulTask = result
}
}
// handle the failed result
func (executor *Executor) processFailedResult(result *Result) {
// fmt.Printf("* Err task id : %s , err : %s , time : %d\n", result.id, result.err, result.time)
executor.summary.failedCount++
if executor.summary.maxTimeTakingFailedTask == nil {
executor.summary.maxTimeTakingFailedTask = result
return
}
if executor.summary.maxTimeTakingFailedTask.time < result.time {
executor.summary.maxTimeTakingFailedTask = result
}
}
// generateSummary generates the summary of the execution
func (executor *Executor) generateSummary() {
// stop listening
// executor.isRunning = false
if executor.summary.maxTimeTakingFailedTask == nil {
executor.summary.maxTimeTakingFailedTask = &Result{
id: "None",
}
}
if executor.summary.maxTimeTakingSuccessfulTask == nil {
executor.summary.maxTimeTakingSuccessfulTask = &Result{
id: "None",
}
}
fmt.Printf("-------------------------------------------------------------------\n")
fmt.Printf("Summary\n")
fmt.Printf("-------------------------------------------------------------------\n\n")
fmt.Printf("Time taken :: %d(ms)\n", (time.Now().UnixNano()-executor.summary.startTime)/1e6)
fmt.Printf("Successful tasks :: %d \n", executor.summary.successCount)
fmt.Printf("Failed tasks :: %d \n", executor.summary.failedCount)
fmt.Printf("Max time taking Successfull task :: %s (%d)ms \n", executor.summary.maxTimeTakingSuccessfulTask.id, executor.summary.maxTimeTakingSuccessfulTask.time)
fmt.Printf("Max time taking Failed task :: %s (%d)ms \n", executor.summary.maxTimeTakingFailedTask.id, executor.summary.maxTimeTakingFailedTask.time)
fmt.Printf("-------------------------------------------------------------------")
}