mirror of
https://github.com/ClusterCockpit/cc-metric-store.git
synced 2024-12-27 09:19:06 +01:00
Use RWLock instead of classical Mutex
This commit is contained in:
parent
63e45960e1
commit
61bc7df93a
12
archive.go
12
archive.go
@ -32,16 +32,16 @@ type ArchiveFile struct {
|
|||||||
func (m *MemoryStore) ToArchive(archiveRoot string, from, to int64) (int, error) {
|
func (m *MemoryStore) ToArchive(archiveRoot string, from, to int64) (int, error) {
|
||||||
levels := make([]*level, 0)
|
levels := make([]*level, 0)
|
||||||
selectors := make([][]string, 0)
|
selectors := make([][]string, 0)
|
||||||
m.root.lock.Lock()
|
m.root.lock.RLock()
|
||||||
for sel1, l1 := range m.root.children {
|
for sel1, l1 := range m.root.children {
|
||||||
l1.lock.Lock()
|
l1.lock.RLock()
|
||||||
for sel2, l2 := range l1.children {
|
for sel2, l2 := range l1.children {
|
||||||
levels = append(levels, l2)
|
levels = append(levels, l2)
|
||||||
selectors = append(selectors, []string{sel1, sel2})
|
selectors = append(selectors, []string{sel1, sel2})
|
||||||
}
|
}
|
||||||
l1.lock.Unlock()
|
l1.lock.RUnlock()
|
||||||
}
|
}
|
||||||
m.root.lock.Unlock()
|
m.root.lock.RUnlock()
|
||||||
|
|
||||||
for i := 0; i < len(levels); i++ {
|
for i := 0; i < len(levels); i++ {
|
||||||
dir := path.Join(archiveRoot, path.Join(selectors[i]...))
|
dir := path.Join(archiveRoot, path.Join(selectors[i]...))
|
||||||
@ -55,8 +55,8 @@ func (m *MemoryStore) ToArchive(archiveRoot string, from, to int64) (int, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *level) toArchiveFile(from, to int64) (*ArchiveFile, error) {
|
func (l *level) toArchiveFile(from, to int64) (*ArchiveFile, error) {
|
||||||
l.lock.Lock()
|
l.lock.RLock()
|
||||||
defer l.lock.Unlock()
|
defer l.lock.RUnlock()
|
||||||
|
|
||||||
retval := &ArchiveFile{
|
retval := &ArchiveFile{
|
||||||
From: from,
|
From: from,
|
||||||
|
30
memstore.go
30
memstore.go
@ -116,31 +116,43 @@ func (b *buffer) read(from, to int64) ([]Float, int64, int64, error) {
|
|||||||
// Can be both a leaf or a inner node. In this tree structue, inner nodes can
|
// Can be both a leaf or a inner node. In this tree structue, inner nodes can
|
||||||
// also hold data (in `metrics`).
|
// also hold data (in `metrics`).
|
||||||
type level struct {
|
type level struct {
|
||||||
lock sync.Mutex // There is performance to be gained by having different locks for `metrics` and `children` (Spinlock?).
|
lock sync.RWMutex //
|
||||||
metrics map[string]*buffer // Every level can store metrics.
|
metrics map[string]*buffer // Every level can store metrics.
|
||||||
children map[string]*level // Sub-granularities/nodes. Use `sync.Map`?
|
children map[string]*level // Lower levels.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caution: the lock of the returned level will be LOCKED.
|
|
||||||
// Find the correct level for the given selector, creating it if
|
// Find the correct level for the given selector, creating it if
|
||||||
// it does not exist. Example selector in the context of the
|
// it does not exist. Example selector in the context of the
|
||||||
// ClusterCockpit could be: []string{ "emmy", "host123", "cpu", "0" }
|
// ClusterCockpit could be: []string{ "emmy", "host123", "cpu", "0" }
|
||||||
// This function would probably benefit a lot from `level.children` beeing a `sync.Map`?
|
// This function would probably benefit a lot from `level.children` beeing a `sync.Map`?
|
||||||
func (l *level) findLevelOrCreate(selector []string) *level {
|
func (l *level) findLevelOrCreate(selector []string) *level {
|
||||||
l.lock.Lock()
|
|
||||||
if len(selector) == 0 {
|
if len(selector) == 0 {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow concurrent reads:
|
||||||
|
l.lock.RLock()
|
||||||
child, ok := l.children[selector[0]]
|
child, ok := l.children[selector[0]]
|
||||||
if !ok {
|
l.lock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return child.findLevelOrCreate(selector[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// The level does not exist, take write lock for unqiue access:
|
||||||
|
l.lock.Lock()
|
||||||
|
// While this thread waited for the write lock, another thread
|
||||||
|
// could have created the child node.
|
||||||
|
child, ok = l.children[selector[0]]
|
||||||
|
if ok {
|
||||||
|
l.lock.Unlock()
|
||||||
|
return child.findLevelOrCreate(selector[1:])
|
||||||
|
}
|
||||||
|
|
||||||
child = &level{
|
child = &level{
|
||||||
metrics: make(map[string]*buffer),
|
metrics: make(map[string]*buffer),
|
||||||
children: make(map[string]*level),
|
children: make(map[string]*level),
|
||||||
}
|
}
|
||||||
l.children[selector[0]] = child
|
l.children[selector[0]] = child
|
||||||
}
|
|
||||||
|
|
||||||
l.lock.Unlock()
|
l.lock.Unlock()
|
||||||
return child.findLevelOrCreate(selector[1:])
|
return child.findLevelOrCreate(selector[1:])
|
||||||
}
|
}
|
||||||
@ -238,6 +250,7 @@ func NewMemoryStore(metrics map[string]MetricConfig) *MemoryStore {
|
|||||||
// Look at `findLevelOrCreate` for how selectors work.
|
// Look at `findLevelOrCreate` for how selectors work.
|
||||||
func (m *MemoryStore) Write(selector []string, ts int64, metrics []Metric) error {
|
func (m *MemoryStore) Write(selector []string, ts int64, metrics []Metric) error {
|
||||||
l := m.root.findLevelOrCreate(selector)
|
l := m.root.findLevelOrCreate(selector)
|
||||||
|
l.lock.Lock()
|
||||||
defer l.lock.Unlock()
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
for _, metric := range metrics {
|
for _, metric := range metrics {
|
||||||
@ -268,7 +281,8 @@ func (m *MemoryStore) Write(selector []string, ts int64, metrics []Metric) error
|
|||||||
|
|
||||||
func (m *MemoryStore) Read(selector []string, metric string, from, to int64) ([]Float, int64, int64, error) {
|
func (m *MemoryStore) Read(selector []string, metric string, from, to int64) ([]Float, int64, int64, error) {
|
||||||
l := m.root.findLevelOrCreate(selector)
|
l := m.root.findLevelOrCreate(selector)
|
||||||
defer l.lock.Unlock()
|
l.lock.RLock()
|
||||||
|
defer l.lock.RUnlock()
|
||||||
|
|
||||||
if from > to {
|
if from > to {
|
||||||
return nil, 0, 0, errors.New("invalid time range")
|
return nil, 0, 0, errors.New("invalid time range")
|
||||||
|
Loading…
Reference in New Issue
Block a user