Skip to content

Commit

Permalink
Add ability to produce a SharedArray from Go code
Browse files Browse the repository at this point in the history
  • Loading branch information
oleiade committed Aug 27, 2024
1 parent 8e782c1 commit d5e6ebc
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 4 deletions.
57 changes: 54 additions & 3 deletions js/modules/k6/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
package data

import (
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
"sync"

Expand Down Expand Up @@ -90,7 +93,55 @@ func (d *Data) sharedArray(call sobek.ConstructorCall) *sobek.Object {
}

array := d.shared.get(rt, name, fn)
return array.wrap(rt).ToObject(rt)
return array.Wrap(rt).ToObject(rt)
}

// Reader is the interface that wraps the basic Read method.
//
// The data module Reader interface is implemented by types that can read data from a source, such
// as a CSV file, etc.
type Reader interface {
Read() ([]string, error)
}

// NewSharedArrayFrom creates a new shared array from the provided data.
//
// This function is not exposed to the JS runtime. It is used internally to instantiate
// shared arrays without having to go through the whole JS runtime machinery, which effectively has
// a big performance impact (e.g. when filling a shared array from a CSV file).
//
// This function takes an explicit runtime argument to retain control over which VU runtime it is
// executed in. This is important because the shared array underlying implementation relies on maintaining
// a single instance of arrays for the whole test setup and VUs.
func (d *Data) NewSharedArrayFrom(rt *sobek.Runtime, name string, r Reader) *sobek.Object {
if name == "" {
common.Throw(rt, errors.New("empty name provided to SharedArray's constructor"))
}

var arr []string
for {
record, err := r.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
common.Throw(rt, fmt.Errorf("failed to read record; reason: %w", err))
}

marshaled, err := json.Marshal(record)
if err != nil {
common.Throw(rt, err)
}

arr = append(arr, string(marshaled))
}

d.shared.mu.Lock()
defer d.shared.mu.Unlock()
array := sharedArray{arr: arr}
d.shared.data[name] = array

return array.Wrap(rt).ToObject(rt)
}

func (s *sharedArrays) get(rt *sobek.Runtime, name string, call sobek.Callable) sharedArray {
Expand Down Expand Up @@ -121,10 +172,10 @@ func getShareArrayFromCall(rt *sobek.Runtime, call sobek.Callable) sharedArray {
}
arr := make([]string, obj.Get("length").ToInteger())

stringify, _ := sobek.AssertFunction(rt.GlobalObject().Get("JSON").ToObject(rt).Get("stringify"))
stringifyFunc, _ := sobek.AssertFunction(rt.GlobalObject().Get("JSON").ToObject(rt).Get("stringify"))
var val sobek.Value
for i := range arr {
val, err = stringify(sobek.Undefined(), obj.Get(strconv.Itoa(i)))
val, err = stringifyFunc(sobek.Undefined(), obj.Get(strconv.Itoa(i)))
if err != nil {
panic(err)
}
Expand Down
3 changes: 2 additions & 1 deletion js/modules/k6/data/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type wrappedSharedArray struct {
parse sobek.Callable
}

func (s sharedArray) wrap(rt *sobek.Runtime) sobek.Value {
func (s sharedArray) Wrap(rt *sobek.Runtime) sobek.Value {
freeze, _ := sobek.AssertFunction(rt.GlobalObject().Get("Object").ToObject(rt).Get("freeze"))
isFrozen, _ := sobek.AssertFunction(rt.GlobalObject().Get("Object").ToObject(rt).Get("isFrozen"))
parse, _ := sobek.AssertFunction(rt.GlobalObject().Get("JSON").ToObject(rt).Get("parse"))
Expand Down Expand Up @@ -49,6 +49,7 @@ func (s wrappedSharedArray) Get(index int) sobek.Value {
if err != nil {
common.Throw(s.rt, err)
}

err = s.deepFreeze(s.rt, val)
if err != nil {
common.Throw(s.rt, err)
Expand Down

0 comments on commit d5e6ebc

Please sign in to comment.