gte/internal/storage/local.go

133 lines
3.5 KiB
Go
Raw Normal View History

2021-06-25 09:14:27 +02:00
package storage
import (
2021-07-10 12:30:38 +02:00
"errors"
2021-07-25 10:02:18 +02:00
"sort"
2021-06-25 09:14:27 +02:00
"time"
2021-09-19 11:59:26 +02:00
"ewintr.nl/gte/internal/task"
2021-06-25 09:14:27 +02:00
)
2021-07-10 12:30:38 +02:00
var (
ErrTaskNotFound = errors.New("task was not found")
)
2021-06-25 09:14:27 +02:00
type LocalRepository interface {
2022-10-25 15:59:09 +02:00
LatestSyncs() (time.Time, time.Time, error) // last fetch, last dispatch, err
2021-06-25 09:14:27 +02:00
SetTasks(tasks []*task.Task) error
2021-08-25 06:52:48 +02:00
FindAll() ([]*task.LocalTask, error)
2021-08-20 09:06:35 +02:00
FindById(id string) (*task.LocalTask, error)
FindByLocalId(id int) (*task.LocalTask, error)
2021-09-04 12:20:35 +02:00
SetLocalUpdate(id string, update *task.LocalUpdate) error
2021-09-03 09:19:36 +02:00
MarkDispatched(id int) error
2021-09-04 12:20:35 +02:00
Add(update *task.LocalUpdate) (*task.LocalTask, error)
2021-07-14 07:17:53 +02:00
}
2021-08-25 06:52:48 +02:00
// NextLocalId finds a new local id by incrememting to a variable limit.
//
// When tasks are edited, some get removed because they are done or deleted.
// It is very confusing if existing tasks get renumbered, or if a new one
// immediatly gets the id of an removed one. So it is better to just
// increment. However, local id's also benefit from being short, so we
// don't want to keep incrementing forever.
//
// This function takes a list if id's that are in use and sets the limit
// to the nearest power of ten depening on the current highest id used.
// The new id is an incremented one from that max. However, if the limit
// is reached, it first tries to find "holes" in the current sequence,
// starting from the bottom. If there are no holes, the limit is increased.
2021-07-14 07:17:53 +02:00
func NextLocalId(used []int) int {
if len(used) == 0 {
return 1
}
2021-07-25 10:02:18 +02:00
sort.Ints(used)
2021-07-14 07:17:53 +02:00
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
2021-06-25 09:14:27 +02:00
}
2021-08-25 06:52:48 +02:00
// MergeNewTaskSet updates a local set of tasks with a remote one
//
2021-09-01 06:52:21 +02:00
// The new set is leading and tasks that are not in there get dismissed. Tasks that
// were created locally and got dispatched might temporarily dissappear if the
2021-08-25 06:52:48 +02:00
// remote inbox has a delay in processing.
func MergeNewTaskSet(oldTasks []*task.LocalTask, newTasks []*task.Task) []*task.LocalTask {
// create lookups
resultMap := map[string]*task.LocalTask{}
for _, nt := range newTasks {
resultMap[nt.Id] = &task.LocalTask{
Task: *nt,
LocalId: 0,
LocalUpdate: &task.LocalUpdate{},
2021-09-01 06:52:21 +02:00
LocalStatus: task.STATUS_FETCHED,
2021-08-25 06:52:48 +02:00
}
}
oldMap := map[string]*task.LocalTask{}
for _, ot := range oldTasks {
oldMap[ot.Id] = ot
}
// apply local id rules:
// - keep id's that were present in the old set
// - find new id's for new tasks
// - assignment of local id's is non deterministic
var used []int
for _, ot := range oldTasks {
if _, ok := resultMap[ot.Id]; ok {
resultMap[ot.Id].LocalId = ot.LocalId
used = append(used, ot.LocalId)
}
}
for id, nt := range resultMap {
if nt.LocalId == 0 {
newLocalId := NextLocalId(used)
resultMap[id].LocalId = newLocalId
used = append(used, newLocalId)
}
}
// apply local update rules:
// - only keep local updates if the new task hasn't moved to a new version yet
for _, ot := range oldTasks {
if nt, ok := resultMap[ot.Id]; ok {
if ot.LocalUpdate.ForVersion >= nt.Version {
resultMap[ot.Id].LocalUpdate = ot.LocalUpdate
2021-09-01 06:52:21 +02:00
resultMap[ot.Id].LocalStatus = task.STATUS_UPDATED
2021-08-25 06:52:48 +02:00
}
}
}
var result []*task.LocalTask
for _, nt := range resultMap {
result = append(result, nt)
}
return result
}