forked from TrueCloudLab/xk6-frostfs
c43f73704e
Includes: 1. Logic simplification: no need to call `ObjSelector.Reset` from JS code, everything could be done inside the Go code. Remove unused mutexes. 2. Do not handle object twice ever: Once handled, any error is expected to be logged on the JS side and never be handled again. It solves "already removed" error. 3. Object caching: no need to call bolt's `View` on every object removal: it blocks other calls and slows down the execution. Read 100 objects (or less if not available yet), cache them and send to buffered channel. Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package registry
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"go.k6.io/k6/js/modules"
|
|
)
|
|
|
|
// RootModule is the global module object type. It is instantiated once per test
|
|
// run and will be used to create k6/x/neofs/registry module instances for each VU.
|
|
type RootModule struct {
|
|
// Stores object registry by path of database file. We should have only single instance
|
|
// of registry per each file
|
|
registries map[string]*ObjRegistry
|
|
// Stores object selector by name. We may have multiple selectors per database file
|
|
selectors map[string]*ObjSelector
|
|
// Mutex to sync access to the maps
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// Registry represents an instance of the module for every VU.
|
|
type Registry struct {
|
|
vu modules.VU
|
|
root *RootModule
|
|
}
|
|
|
|
// Ensure the interfaces are implemented correctly.
|
|
var (
|
|
_ modules.Instance = &Registry{}
|
|
_ modules.Module = &RootModule{}
|
|
)
|
|
|
|
func init() {
|
|
rootModule := &RootModule{
|
|
registries: make(map[string]*ObjRegistry),
|
|
selectors: make(map[string]*ObjSelector),
|
|
}
|
|
modules.Register("k6/x/neofs/registry", rootModule)
|
|
}
|
|
|
|
// NewModuleInstance implements the modules.Module interface and returns
|
|
// a new instance for each VU.
|
|
func (r *RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
|
|
mi := &Registry{vu: vu, root: r}
|
|
return mi
|
|
}
|
|
|
|
// Exports implements the modules.Instance interface and returns the exports
|
|
// of the JS module.
|
|
func (r *Registry) Exports() modules.Exports {
|
|
return modules.Exports{Default: r}
|
|
}
|
|
|
|
// Open creates a new instance of object registry that will store information about objects
|
|
// in the specified file. If repository instance for the file was previously created, then
|
|
// Open will return the existing instance of repository, because bolt database allows only
|
|
// one write connection at a time.
|
|
func (r *Registry) Open(dbFilePath string) *ObjRegistry {
|
|
r.root.mu.Lock()
|
|
defer r.root.mu.Unlock()
|
|
return r.open(dbFilePath)
|
|
}
|
|
|
|
// Implementation of Open without mutex lock, so that it can be re-used in other methods.
|
|
func (r *Registry) open(dbFilePath string) *ObjRegistry {
|
|
registry := r.root.registries[dbFilePath]
|
|
if registry == nil {
|
|
registry = NewObjRegistry(r.vu.Context(), dbFilePath)
|
|
r.root.registries[dbFilePath] = registry
|
|
}
|
|
return registry
|
|
}
|
|
|
|
func (r *Registry) GetSelector(dbFilePath string, name string, filter map[string]string) *ObjSelector {
|
|
objFilter, err := parseFilter(filter)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
r.root.mu.Lock()
|
|
defer r.root.mu.Unlock()
|
|
|
|
selector := r.root.selectors[name]
|
|
if selector == nil {
|
|
registry := r.open(dbFilePath)
|
|
selector = NewObjSelector(registry, objFilter)
|
|
r.root.selectors[name] = selector
|
|
} else if !reflect.DeepEqual(selector.filter, objFilter) {
|
|
panic(fmt.Sprintf("selector %s already has been created with a different filter", name))
|
|
}
|
|
return selector
|
|
}
|
|
|
|
func parseFilter(filter map[string]string) (*ObjFilter, error) {
|
|
objFilter := ObjFilter{}
|
|
objFilter.Status = filter["status"]
|
|
|
|
if ageStr := filter["age"]; ageStr != "" {
|
|
age, err := strconv.ParseInt(ageStr, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objFilter.Age = int(age)
|
|
}
|
|
|
|
return &objFilter, nil
|
|
}
|