Skip to content

Commit dba70bc

Browse files
authored
Workflow data parallelism updates and bugfixes (#263)
* Fixed parallel execution for single-function tasks. * Parallel execution for single-function tasks bugfixes. * Adapted updated store_requests function. * Added fetch/store support for fused operators.
1 parent 48e3391 commit dba70bc

7 files changed

Lines changed: 83 additions & 82 deletions

File tree

internal/control_plane/workflow/dandelion-workflow/partition.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,23 @@ func fullPartition(c *Composition, wf *workflow.Workflow) []*workflow.Task {
216216
stmtToTask := make(map[*Statement]*workflow.Task)
217217
for stmtIdx, stmt := range c.Statements {
218218
task := &workflow.Task{
219-
Name: fmt.Sprintf("%s_%s%d", wf.Name, stmt.Name, stmtIdx),
220-
Functions: []string{stmt.Name},
221-
NumIn: uint32(len(stmt.Args)),
222-
NumOut: uint32(len(stmt.Rets)),
223-
InputSharding: shardingFromInDescList(stmt.Args),
219+
Name: fmt.Sprintf("%s_%s%d", wf.Name, stmt.Name, stmtIdx),
220+
Functions: []string{stmt.Name},
221+
NumIn: uint32(len(stmt.Args)),
222+
NumOut: uint32(len(stmt.Rets)),
223+
InputSharding: shardingFromInDescList(stmt.Args),
224+
FunctionInNum: []int32{int32(len(stmt.Args))},
225+
FunctionOutNum: []int32{int32(len(stmt.Rets))},
226+
FunctionInSharding: shardingFromInDescList(stmt.Args),
227+
FunctionDataFlow: make([]int32, 2*(len(stmt.Args)+len(stmt.Rets))),
228+
}
229+
for i := 0; i < len(stmt.Args); i++ {
230+
task.FunctionDataFlow[i*2] = -1
231+
task.FunctionDataFlow[i*2+1] = int32(i)
232+
}
233+
for i := 0; i < len(stmt.Rets); i++ {
234+
task.FunctionDataFlow[(i+len(stmt.Args))*2] = 0
235+
task.FunctionDataFlow[(i+len(stmt.Args))*2+1] = int32(i)
224236
}
225237
stmtToTask[stmt] = task
226238
tasks = append(tasks, task)

internal/control_plane/workflow/workflow.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ type Task struct {
1818
InputSharding []Sharding
1919

2020
Functions []string // functions belonging to this task
21-
FunctionInNum []int32 // # args per function (if > 1 function)
22-
FunctionOutNum []int32 // # returns per function (if > 1 function)
23-
FunctionDataFlow []int32 // (src func idx, arg idx) for each function input + task outputs describing internal dataflow (if > 1 function)
21+
FunctionInNum []int32 // # args per function
22+
FunctionOutNum []int32 // # returns per function
23+
FunctionDataFlow []int32 // (src func idx, arg idx) for each function input + task outputs describing internal dataflow
2424
FunctionInSharding []Sharding // sharding for each function input
2525

2626
ConsumerTasks []*Task // tasks consuming output of this task

internal/data_plane/proxy/handler.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,11 @@ func scheduleWorkflowFunction(httpClient *http.Client, proxyCtx *proxyContext) s
158158
startTime := time.Now()
159159
task := schedulerTask.GetTask()
160160
var serviceName string
161-
if len(task.Functions) > 1 {
162-
serviceName = task.Name
163-
} else {
161+
// no need to register separate task for single function without parallelism
162+
if len(task.Functions) == 1 && !task.IsParallel() {
164163
serviceName = task.Functions[0]
164+
} else {
165+
serviceName = task.Name
165166
}
166167

167168
// metadata fetching

internal/data_plane/service_metadata/deployment.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,13 @@ func (d *Deployments) AddWorkflowDeployment(name string, dataplaneID string, wf
7272

7373
// TODO: make container concurrency configurable
7474
for _, task := range wf.Tasks {
75-
if len(task.Functions) > 1 {
76-
d.data[task.Name] = &Deployment{
77-
dType: Task,
78-
metadata: NewFunctionMetadata(task.Name, dataplaneID, 1),
79-
}
75+
// no need to register separate task for single function without parallelism
76+
if len(task.Functions) == 1 && !task.IsParallel() {
77+
continue
78+
}
79+
d.data[task.Name] = &Deployment{
80+
dType: Task,
81+
metadata: NewFunctionMetadata(task.Name, dataplaneID, 1),
8082
}
8183
}
8284

internal/data_plane/workflow/workflow.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ type Task struct {
3131
ConsumerDataDstIdx []int32 // argument idx in consumer (where to put the data)
3232
}
3333

34+
func (t *Task) IsParallel() bool {
35+
for _, s := range t.InputSharding {
36+
if s != ShardingAll && s != ShardingAny {
37+
return true
38+
}
39+
}
40+
return false
41+
}
42+
3443
type Workflow struct {
3544
Name string
3645
Tasks []*Task

internal/worker_node/sandbox/dandelion/runtime.go

Lines changed: 38 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import (
2121
"slices"
2222
)
2323

24-
var dandelionOperators = []string{"aggregate", "csv_reader", "csv_writer", "fetch", "filter", "hash_join", "project", "order", "splitter"}
24+
var dandelionOperators = []string{
25+
"aggregate", "csv_reader", "csv_writer", "fetch", "filter", "hash_join", "project", "order", "splitter",
26+
"fused_csv_reader", "fused_csv_writer",
27+
}
2528

2629
type registeredServices struct {
2730
data map[string]bool
@@ -149,7 +152,7 @@ func (dr *Runtime) exportFunctionComposition(task *proto.WorkflowTaskInfo) strin
149152
if fIdx == 0 {
150153
funDecls += "function fetch_requests(url) => (getRequest);"
151154
funDecls += "function HTTP(request) => (response);"
152-
if f == "csv_reader_op" { // -> reader: 2nd input -> data
155+
if f == "csv_reader_op" || f == "fused_csv_reader_op" { // -> reader: 2nd input -> data
153156
srcDataIdx := task.FunctionDataFlow[3]
154157
task.FunctionDataFlow[2] = -2
155158
funAppls += fmt.Sprintf("fetch_requests(url = each cIn%d) => (inDataReq = getRequest); ", srcDataIdx)
@@ -224,20 +227,18 @@ func (dr *Runtime) exportFunctionComposition(task *proto.WorkflowTaskInfo) strin
224227
// operator store
225228
if addLoadAndStore && fIdx == len(task.Functions)-1 {
226229
inUrlSet := fmt.Sprintf("cIn%d", task.FunctionDataFlow[5])
227-
if task.Functions[0] == "csv_reader" {
230+
if task.Functions[0] == "csv_reader" || task.Functions[0] == "fused_csv_reader" {
228231
inUrlSet = fmt.Sprintf("cIn%d", task.FunctionDataFlow[3])
229232
}
230233

231-
if f == "csv_writer_op" {
232-
funDecls += "function store_requests_data(inUrl, data) => (putRequest, outUrl);"
233-
funAppls += fmt.Sprintf(" store_requests_data(inUrl = all %s, data = each f%dd0) => (outDataReq = putRequest, outDataUrl = outUrl); ", inUrlSet, fIdx)
234+
funDecls += "function store_requests(inUrl, data) => (putRequest, outUrl);"
235+
if f == "csv_writer_op" || f == "fused_csv_writer_op" {
236+
funAppls += fmt.Sprintf(" store_requests(inUrl = all %s, data = each f%dd0) => (outDataReq = putRequest, outDataUrl = outUrl); ", inUrlSet, fIdx)
234237
funAppls += "HTTP(request = each outDataReq) => (_0 = response);"
235238
} else {
236-
funDecls += "function store_requests_schema(inUrl, data) => (putRequest, outUrl);"
237-
funDecls += "function store_requests_batches(inUrl, data) => (putRequest, outUrl);"
238-
funAppls += fmt.Sprintf(" store_requests_schema(inUrl = all %s, data = each f%dd0) => (outSchemaReq = putRequest, outSchemaUrl = outUrl); ", inUrlSet, fIdx)
239+
funAppls += fmt.Sprintf(" store_requests(inUrl = all %s, data = each f%dd0) => (outSchemaReq = putRequest, outSchemaUrl = outUrl); ", inUrlSet, fIdx)
239240
funAppls += "HTTP(request = each outSchemaReq) => (_0 = response); "
240-
funAppls += fmt.Sprintf("store_requests_batches(inUrl = all %s, data = each f%dd1) => (outBatchesReq = putRequest, outBatchesUrl = outUrl); ", inUrlSet, fIdx)
241+
funAppls += fmt.Sprintf("store_requests(inUrl = all %s, data = each f%dd1) => (outBatchesReq = putRequest, outBatchesUrl = outUrl); ", inUrlSet, fIdx)
241242
funAppls += "HTTP(request = each outBatchesReq) => (_1 = response);"
242243
}
243244
}
@@ -255,7 +256,7 @@ func (dr *Runtime) exportFunctionComposition(task *proto.WorkflowTaskInfo) strin
255256
// composition output
256257
compOut := ""
257258
if addLoadAndStore {
258-
if task.Functions[len(task.Functions)-1] == "csv_writer" {
259+
if task.Functions[len(task.Functions)-1] == "csv_writer" || task.Functions[len(task.Functions)-1] == "fused_csv_writer" {
259260
compOut = "outDataUrl"
260261
} else {
261262
compOut = "outSchemaUrl, outBatchesUrl"
@@ -287,54 +288,52 @@ func addOperatorReadStore(operator string, withStdio bool) (string, string) {
287288

288289
var outComposition string
289290
switch operator {
290-
case "csv_reader":
291+
case "csv_reader", "fused_csv_reader":
291292
outComposition = fmt.Sprintf(
292-
`function csv_reader_op(options, inData) => (outSchema, outBatches%s);
293+
`function %s_op(options, inData) => (outSchema, outBatches%s);
293294
function fetch_requests(url) => (getRequest);
294-
function store_requests_schema(inUrl, data) => (putRequest, outUrl);
295-
function store_requests_batches(inUrl, data) => (putRequest, outUrl);
295+
function store_requests(inUrl, data) => (putRequest, outUrl);
296296
function HTTP(request) => (response);
297297
298-
composition csv_reader (opOptions, inDataUrl) => (outSchemaUrl, outBatchesUrl%s) {
298+
composition %s (opOptions, inDataUrl) => (outSchemaUrl, outBatchesUrl%s) {
299299
fetch_requests(url = all inDataUrl) => (inDataReq = getRequest);
300300
HTTP(request = all inDataReq) => (fetchedData = response);
301301
302-
csv_reader_op (options = all opOptions, inData = all fetchedData)
302+
%s_op (options = all opOptions, inData = all fetchedData)
303303
=> (outSchema = outSchema, outBatches = outBatches%s);
304304
305-
store_requests_schema(inUrl = all inDataUrl, data = all outSchema)
305+
store_requests(inUrl = all inDataUrl, data = all outSchema)
306306
=> (outSchemaReq = putRequest, outSchemaUrl = outUrl);
307307
HTTP(request = all outSchemaReq) => (_0 = response);
308-
store_requests_batches(inUrl = all inDataUrl, data = all outBatches)
308+
store_requests(inUrl = all inDataUrl, data = all outBatches)
309309
=> (outBatchesReq = putRequest, outBatchesUrl = outUrl);
310310
HTTP(request = all outBatchesReq) => (_1 = response);
311-
}`, stdioOpDecl, stdioCompDef, stdioOpCall)
312-
case "csv_writer":
311+
}`, operator, stdioOpDecl, operator, stdioCompDef, operator, stdioOpCall)
312+
case "csv_writer", "fused_csv_writer":
313313
outComposition = fmt.Sprintf(
314-
`function csv_writer_op(options, inSchema, inBatches) => (outData%s);
314+
`function %s_op(options, inSchema, inBatches) => (outData%s);
315315
function fetch_requests(url) => (getRequest);
316-
function store_requests_data(inUrl, data) => (putRequest, outUrl);
316+
function store_requests(inUrl, data) => (putRequest, outUrl);
317317
function HTTP(request) => (response);
318318
319-
composition csv_writer (opOptions, inSchemaUrl, inBatchesUrl) => (outDataUrl%s) {
319+
composition %s (opOptions, inSchemaUrl, inBatchesUrl) => (outDataUrl%s) {
320320
fetch_requests(url = all inSchemaUrl) => (inSchemaReq = getRequest);
321321
HTTP(request = all inSchemaReq) => (fetchedSchema = response);
322322
fetch_requests(url = all inBatchesUrl) => (inBatchesReq = getRequest);
323323
HTTP(request = all inBatchesReq) => (fetchedBatches = response);
324324
325-
csv_writer_op(options = all opOptions, inSchema = all fetchedSchema, inBatches = all fetchedBatches)
325+
%s_op(options = all opOptions, inSchema = all fetchedSchema, inBatches = all fetchedBatches)
326326
=> (outData = outData%s);
327327
328-
store_requests_data(inUrl = all inBatchesUrl, data = all outData)
328+
store_requests(inUrl = all inBatchesUrl, data = all outData)
329329
=> (outDataReq = putRequest, outDataUrl = outUrl);
330330
HTTP(request = all outDataReq) => (_0 = response);
331-
}`, stdioOpDecl, stdioCompDef, stdioOpCall)
331+
}`, operator, stdioOpDecl, operator, stdioCompDef, operator, stdioOpCall)
332332
case "hash_join":
333333
outComposition = fmt.Sprintf(
334334
`function hash_join_op(options, inSchema, inBatches, inSchema2, inBatches2) => (outSchema, outBatches%s);
335335
function fetch_requests(url) => (getRequest);
336-
function store_requests_schema(inUrl, data) => (putRequest, outUrl);
337-
function store_requests_batches(inUrl, data) => (putRequest, outUrl);
336+
function store_requests(inUrl, data) => (putRequest, outUrl);
338337
function HTTP(request) => (response);
339338
340339
composition hash_join (opOptions, inSchemaUrl, inBatchesUrl, inSchemaUrl2, inBatchesUrl2) => (outSchemaUrl, outBatchesUrl%s) {
@@ -350,19 +349,18 @@ func addOperatorReadStore(operator string, withStdio bool) (string, string) {
350349
hash_join_op(options = all opOptions, inSchema = all fetchedSchema, inBatches = all fetchedBatches, inSchema2 = all fetchedSchema2, inBatches2 = all fetchedBatches2)
351350
=> (outSchema = outSchema, outBatches = outBatches%s);
352351
353-
store_requests_schema(inUrl = all inBatchesUrl, data = all outSchema)
352+
store_requests(inUrl = all inBatchesUrl, data = all outSchema)
354353
=> (outSchemaReq = putRequest, outSchemaUrl = outUrl);
355354
HTTP(request = all outSchemaReq) => (_0 = response);
356-
store_requests_batches(inUrl = all inBatchesUrl, data = all outBatches)
355+
store_requests(inUrl = all inBatchesUrl, data = all outBatches)
357356
=> (outBatchesReq = putRequest, outBatchesUrl = outUrl);
358357
HTTP(request = all outBatchesReq) => (_1 = response);
359358
}`, stdioOpDecl, stdioCompDef, stdioOpCall)
360359
default:
361360
outComposition = fmt.Sprintf(
362361
`function %s_op(options, inSchema, inBatches) => (outSchema, outBatches%s);
363362
function fetch_requests(url) => (getRequest);
364-
function store_requests_schema(inUrl, data) => (putRequest, outUrl);
365-
function store_requests_batches(inUrl, data) => (putRequest, outUrl);
363+
function store_requests(inUrl, data) => (putRequest, outUrl);
366364
function HTTP(request) => (response);
367365
368366
composition %s (opOptions, inSchemaUrl, inBatchesUrl) => (outSchemaUrl, outBatchesUrl%s) {
@@ -374,10 +372,10 @@ func addOperatorReadStore(operator string, withStdio bool) (string, string) {
374372
%s_op(options = all opOptions, inSchema = all fetchedSchema, inBatches = all fetchedBatches)
375373
=> (outSchema = outSchema, outBatches = outBatches%s);
376374
377-
store_requests_schema(inUrl = all inBatchesUrl, data = all outSchema)
375+
store_requests(inUrl = all inBatchesUrl, data = all outSchema)
378376
=> (outSchemaReq = putRequest, outSchemaUrl = outUrl);
379377
HTTP(request = all outSchemaReq) => (_0 = response);
380-
store_requests_batches(inUrl = all inBatchesUrl, data = all outBatches)
378+
store_requests(inUrl = all inBatchesUrl, data = all outBatches)
381379
=> (outBatchesReq = putRequest, outBatchesUrl = outUrl);
382380
HTTP(request = all outBatchesReq) => (_1 = response);
383381
}`, operator, stdioOpDecl, operator, stdioCompDef, operator, stdioOpCall)
@@ -387,7 +385,7 @@ func addOperatorReadStore(operator string, withStdio bool) (string, string) {
387385
return fmt.Sprintf("%s_op", operator), outComposition
388386
}
389387

390-
func (dr *Runtime) registerHelpers(ctx context.Context) (*proto.SandboxCreationStatus, error) {
388+
func (dr *Runtime) registerRequestBuilders(ctx context.Context) (*proto.SandboxCreationStatus, error) {
391389
dr.registeredFunctions.RLock()
392390
_, ok := dr.registeredFunctions.data["fetch_requests"]
393391
dr.registeredFunctions.RUnlock()
@@ -406,28 +404,8 @@ func (dr *Runtime) registerHelpers(ctx context.Context) (*proto.SandboxCreationS
406404
}
407405

408406
status, err = dr.CreateSandbox(ctx, &proto.ServiceInfo{
409-
Name: "store_requests_schema",
410-
Image: "/users/tstocker/operators/ops_export/store_requests_schema",
411-
NumArgs: 2,
412-
NumRets: 2,
413-
})
414-
if err != nil {
415-
return status, err
416-
}
417-
418-
status, err = dr.CreateSandbox(ctx, &proto.ServiceInfo{
419-
Name: "store_requests_batches",
420-
Image: "/users/tstocker/operators/ops_export/store_requests_batches",
421-
NumArgs: 2,
422-
NumRets: 2,
423-
})
424-
if err != nil {
425-
return status, err
426-
}
427-
428-
status, err = dr.CreateSandbox(ctx, &proto.ServiceInfo{
429-
Name: "store_requests_data",
430-
Image: "/users/tstocker/operators/ops_export/store_requests_data",
407+
Name: "store_requests",
408+
Image: "/users/tstocker/operators/ops_export/store_requests",
431409
NumArgs: 2,
432410
NumRets: 2,
433411
})
@@ -463,7 +441,7 @@ func (dr *Runtime) CreateSandbox(ctx context.Context, in *proto.ServiceInfo) (*p
463441
fName := in.Name
464442
var opComposition string
465443
if dr.dandelionConfig.AddOperatorLoadAndStore && slices.Contains(dandelionOperators, fName) {
466-
s, e := dr.registerHelpers(ctx)
444+
s, e := dr.registerRequestBuilders(ctx)
467445
if e != nil {
468446
return s, e
469447
}
@@ -564,7 +542,7 @@ func (dr *Runtime) CreateTaskSandbox(ctx context.Context, task *proto.WorkflowTa
564542
firstIsOperator := slices.Contains(dandelionOperators, task.Functions[0])
565543
lastIsOperator := slices.Contains(dandelionOperators, task.Functions[len(task.Functions)-1])
566544
if firstIsOperator && lastIsOperator {
567-
s, e := dr.registerHelpers(ctx)
545+
s, e := dr.registerRequestBuilders(ctx)
568546
if e != nil {
569547
return s, e
570548
}

internal/worker_node/sandbox/dandelion/runtime_test.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,15 @@ func TestExportFunctionCompositionLoadStore1(t *testing.T) {
164164
"function HTTP(request) => (response);" +
165165
"function csv_reader_op (in0, in1) => (out0, out1);" +
166166
"function filter_op (in0, in1, in2) => (out0, out1);" +
167-
"function store_requests_schema(inUrl, data) => (putRequest, outUrl);" +
168-
"function store_requests_batches(inUrl, data) => (putRequest, outUrl); " +
167+
"function store_requests(inUrl, data) => (putRequest, outUrl); " +
169168
"composition Test (cIn0, cIn1, cIn2) => (outSchemaUrl, outBatchesUrl) {" +
170169
"fetch_requests(url = each cIn2) => (inDataReq = getRequest); " +
171170
"HTTP(request = each inDataReq) => (fetched2 = response); " +
172171
"csv_reader_op (in0 = all cIn0, in1 = each fetched2) => (f0d0 = out0, f0d1 = out1); " +
173172
"filter_op (in0 = all cIn1, in1 = all f0d0, in2 = each f0d1) => (f1d0 = out0, f1d1 = out1); " +
174-
"store_requests_schema(inUrl = all cIn2, data = each f1d0) => (outSchemaReq = putRequest, outSchemaUrl = outUrl); " +
173+
"store_requests(inUrl = all cIn2, data = each f1d0) => (outSchemaReq = putRequest, outSchemaUrl = outUrl); " +
175174
"HTTP(request = each outSchemaReq) => (_0 = response); " +
176-
"store_requests_batches(inUrl = all cIn2, data = each f1d1) => (outBatchesReq = putRequest, outBatchesUrl = outUrl); " +
175+
"store_requests(inUrl = all cIn2, data = each f1d1) => (outBatchesReq = putRequest, outBatchesUrl = outUrl); " +
177176
"HTTP(request = each outBatchesReq) => (_1 = response);" +
178177
"}"
179178
if exportWithLoadAndStore != expectedWithLoadAndStore {
@@ -224,7 +223,7 @@ func TestExportFunctionCompositionLoadStore2(t *testing.T) {
224223
"function hash_join_op (in0, in1, in2, in3, in4) => (out0, out1);" +
225224
"function project_op (in0, in1, in2) => (out0, out1);" +
226225
"function csv_writer_op (in0, in1, in2) => (out0);" +
227-
"function store_requests_data(inUrl, data) => (putRequest, outUrl); " +
226+
"function store_requests(inUrl, data) => (putRequest, outUrl); " +
228227
"composition Test (cIn0, cIn1, cIn2, cIn3, cIn4, cIn5, cIn6) => (outDataUrl) {" +
229228
"fetch_requests(url = each cIn3) => (inReq3 = getRequest); " +
230229
"HTTP(request = each inReq3) => (fetched3 = response); " +
@@ -237,7 +236,7 @@ func TestExportFunctionCompositionLoadStore2(t *testing.T) {
237236
"hash_join_op (in0 = all cIn0, in1 = all fetched3, in2 = all fetched4, in3 = all fetched5, in4 = all fetched6) => (f0d0 = out0, f0d1 = out1); " +
238237
"project_op (in0 = all cIn1, in1 = all f0d0, in2 = each f0d1) => (f1d0 = out0, f1d1 = out1); " +
239238
"csv_writer_op (in0 = all cIn2, in1 = all f1d0, in2 = each f1d1) => (f2d0 = out0); " +
240-
"store_requests_data(inUrl = all cIn4, data = each f2d0) => (outDataReq = putRequest, outDataUrl = outUrl); " +
239+
"store_requests(inUrl = all cIn4, data = each f2d0) => (outDataReq = putRequest, outDataUrl = outUrl); " +
241240
"HTTP(request = each outDataReq) => (_0 = response);" +
242241
"}"
243242
if exportWithLoadAndStore != expectedWithLoadAndStore {

0 commit comments

Comments
 (0)