local id
This commit is contained in:
parent
cd784f999d
commit
841b866e3f
|
@ -10,12 +10,14 @@ import (
|
||||||
|
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
events map[string]item.Event
|
events map[string]item.Event
|
||||||
|
localIDs map[int]string
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemory() *Memory {
|
func NewMemory() *Memory {
|
||||||
return &Memory{
|
return &Memory{
|
||||||
events: make(map[string]item.Event),
|
events: make(map[string]item.Event),
|
||||||
|
localIDs: make(map[int]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +32,23 @@ func (r *Memory) Find(id string) (item.Event, error) {
|
||||||
return event, nil
|
return event, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Memory) FindAll() ([]item.Event, error) {
|
func (r *Memory) FindByLocal(localID int) (item.Event, error) {
|
||||||
|
r.mutex.RLock()
|
||||||
|
defer r.mutex.RUnlock()
|
||||||
|
|
||||||
|
id, exists := r.localIDs[localID]
|
||||||
|
if !exists {
|
||||||
|
return item.Event{}, errors.New("event not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
event, exists := r.events[id]
|
||||||
|
if !exists {
|
||||||
|
return item.Event{}, errors.New("id an localid mismatch")
|
||||||
|
}
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Memory) FindAll() (map[int]string, []item.Event, error) {
|
||||||
r.mutex.RLock()
|
r.mutex.RLock()
|
||||||
defer r.mutex.RUnlock()
|
defer r.mutex.RUnlock()
|
||||||
|
|
||||||
|
@ -42,14 +60,24 @@ func (r *Memory) FindAll() ([]item.Event, error) {
|
||||||
return events[i].ID < events[j].ID
|
return events[i].ID < events[j].ID
|
||||||
})
|
})
|
||||||
|
|
||||||
return events, nil
|
return r.localIDs, events, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Memory) Store(e item.Event) error {
|
func (r *Memory) Store(e item.Event) error {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
|
|
||||||
|
if _, exists := r.events[e.ID]; !exists {
|
||||||
|
cur := make([]int, 0, len(r.localIDs))
|
||||||
|
for i := range r.localIDs {
|
||||||
|
cur = append(cur, i)
|
||||||
|
}
|
||||||
|
localID := NextLocalID(cur)
|
||||||
|
r.localIDs[localID] = e.ID
|
||||||
|
}
|
||||||
|
|
||||||
r.events[e.ID] = e
|
r.events[e.ID] = e
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,5 +89,12 @@ func (r *Memory) Delete(id string) error {
|
||||||
return errors.New("event not found")
|
return errors.New("event not found")
|
||||||
}
|
}
|
||||||
delete(r.events, id)
|
delete(r.events, id)
|
||||||
|
|
||||||
|
for localID, eventID := range r.localIDs {
|
||||||
|
if id == eventID {
|
||||||
|
delete(r.localIDs, localID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import "go-mod.ewintr.nl/planner/item"
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/item"
|
||||||
|
)
|
||||||
|
|
||||||
type EventRepo interface {
|
type EventRepo interface {
|
||||||
Store(event item.Event) error
|
Store(event item.Event) error
|
||||||
|
@ -8,3 +12,39 @@ type EventRepo interface {
|
||||||
FindAll() ([]item.Event, error)
|
FindAll() ([]item.Event, error)
|
||||||
Delete(id string) error
|
Delete(id string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NextLocalID(used []int) int {
|
||||||
|
if len(used) == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Ints(used)
|
||||||
|
usedMax := 1
|
||||||
|
for _, u := range used {
|
||||||
|
if u > usedMax {
|
||||||
|
usedMax = u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var limit int
|
||||||
|
for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
|
||||||
|
}
|
||||||
|
|
||||||
|
newId := used[len(used)-1] + 1
|
||||||
|
if newId < limit {
|
||||||
|
return newId
|
||||||
|
}
|
||||||
|
|
||||||
|
usedMap := map[int]bool{}
|
||||||
|
for _, u := range used {
|
||||||
|
usedMap[u] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < limit; i++ {
|
||||||
|
if _, ok := usedMap[i]; !ok {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return limit
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package storage_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go-mod.ewintr.nl/planner/plan/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNextLocalId(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
used []int
|
||||||
|
exp int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
used: []int{},
|
||||||
|
exp: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not empty",
|
||||||
|
used: []int{5},
|
||||||
|
exp: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple",
|
||||||
|
used: []int{2, 3, 4},
|
||||||
|
exp: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "holes",
|
||||||
|
used: []int{1, 5, 8},
|
||||||
|
exp: 9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expand limit",
|
||||||
|
used: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
|
exp: 11,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrap if possible",
|
||||||
|
used: []int{8, 9},
|
||||||
|
exp: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "find hole",
|
||||||
|
used: []int{1, 2, 3, 4, 5, 7, 8, 9},
|
||||||
|
exp: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dont wrap if expanded before",
|
||||||
|
used: []int{15, 16},
|
||||||
|
exp: 17,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "do wrap if expanded limit is reached",
|
||||||
|
used: []int{99},
|
||||||
|
exp: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sync bug",
|
||||||
|
used: []int{151, 956, 955, 150, 154, 155, 145, 144,
|
||||||
|
136, 152, 148, 146, 934, 149, 937, 135, 140, 139,
|
||||||
|
143, 137, 153, 939, 138, 953, 147, 141, 938, 142,
|
||||||
|
},
|
||||||
|
exp: 957,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
act := storage.NextLocalID(tc.used)
|
||||||
|
if tc.exp != act {
|
||||||
|
t.Errorf("exp %v, got %v", tc.exp, act)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue