github.com/ngicks/gokugen@v0.0.5/impl/repository/in_memory_repository.go (about) 1 package repository 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "sort" 7 "sync" 8 "time" 9 10 "github.com/ngicks/gokugen/common" 11 taskstorage "github.com/ngicks/gokugen/task_storage" 12 syncparam "github.com/ngicks/type-param-common/sync-param" 13 ) 14 15 var _ taskstorage.RepositoryUpdater = &InMemoryRepo{} 16 17 type ent struct { 18 mu sync.Mutex 19 info taskstorage.TaskInfo 20 } 21 22 func (e *ent) Update(new taskstorage.TaskState, updateIf func(old taskstorage.TaskState) bool, getNow common.GetNower) bool { 23 e.mu.Lock() 24 defer e.mu.Unlock() 25 if updateIf(e.info.State) { 26 e.info.State = new 27 e.info.LastModified = getNow.GetNow() 28 return true 29 } 30 return false 31 } 32 33 func (e *ent) UpdateByDiff(diff taskstorage.UpdateDiff, getNow common.GetNower) bool { 34 e.mu.Lock() 35 defer e.mu.Unlock() 36 37 if !isUpdatable(e.info.State) { 38 return false 39 } 40 41 e.info.LastModified = getNow.GetNow() 42 43 if diff.UpdateKey.WorkId { 44 e.info.WorkId = diff.Diff.WorkId 45 } 46 if diff.UpdateKey.Param { 47 e.info.Param = diff.Diff.Param 48 } 49 if diff.UpdateKey.ScheduledTime { 50 e.info.ScheduledTime = diff.Diff.ScheduledTime 51 } 52 if diff.UpdateKey.State { 53 e.info.State = diff.Diff.State 54 } 55 return true 56 } 57 58 type InMemoryRepo struct { 59 randomStr *RandStringGenerator 60 store *syncparam.Map[string, *ent] 61 getNow common.GetNower 62 } 63 64 func NewInMemoryRepo() *InMemoryRepo { 65 return &InMemoryRepo{ 66 randomStr: NewRandStringGenerator(int64(time.Now().Nanosecond()), 16, hex.NewEncoder), 67 store: new(syncparam.Map[string, *ent]), 68 getNow: common.GetNowImpl{}, 69 } 70 } 71 72 func (r *InMemoryRepo) Insert(taskInfo taskstorage.TaskInfo) (taskId string, err error) { 73 for { 74 taskId, err = r.randomStr.Generate() 75 if err != nil { 76 return 77 } 78 79 taskInfo.Id = taskId 80 taskInfo.LastModified = r.getNow.GetNow() 81 82 _, loaded := r.store.LoadOrStore(taskId, &ent{info: taskInfo}) 83 84 if !loaded { 85 break 86 } 87 } 88 return 89 } 90 91 func (r *InMemoryRepo) GetAll() ([]taskstorage.TaskInfo, error) { 92 arr := make([]taskstorage.TaskInfo, 0) 93 r.store.Range(func(key string, value *ent) bool { 94 value.mu.Lock() 95 defer value.mu.Unlock() 96 arr = append(arr, value.info) 97 return true 98 }) 99 100 sort.Slice(arr, func(i, j int) bool { 101 return arr[i].LastModified.Before(arr[j].LastModified) 102 }) 103 return arr, nil 104 } 105 106 func (r *InMemoryRepo) GetUpdatedSince(since time.Time) ([]taskstorage.TaskInfo, error) { 107 results := make([]taskstorage.TaskInfo, 0) 108 r.store.Range(func(key string, entry *ent) bool { 109 entry.mu.Lock() 110 defer entry.mu.Unlock() 111 if entry.info.LastModified.After(since) || entry.info.LastModified.Equal(since) { 112 results = append(results, entry.info) 113 } 114 return true 115 }) 116 117 sort.Slice(results, func(i, j int) bool { 118 return results[i].LastModified.Before(results[j].LastModified) 119 }) 120 return results, nil 121 } 122 123 func (r *InMemoryRepo) GetById(taskId string) (taskstorage.TaskInfo, error) { 124 val, ok := r.store.Load(taskId) 125 if !ok { 126 return taskstorage.TaskInfo{}, fmt.Errorf("%w: no such id [%s]", taskstorage.ErrNoEnt, taskId) 127 } 128 return val.info, nil 129 } 130 131 func (r *InMemoryRepo) MarkAsDone(id string) (ok bool, err error) { 132 return updateState(r.store, id, taskstorage.Done, r.getNow) 133 } 134 func (r *InMemoryRepo) MarkAsCancelled(id string) (ok bool, err error) { 135 return updateState(r.store, id, taskstorage.Cancelled, r.getNow) 136 } 137 func (r *InMemoryRepo) MarkAsFailed(id string) (ok bool, err error) { 138 return updateState(r.store, id, taskstorage.Failed, r.getNow) 139 } 140 141 func (r *InMemoryRepo) UpdateState(id string, old, new taskstorage.TaskState) (swapped bool, err error) { 142 entry, ok := r.store.Load(id) 143 if !ok { 144 return false, fmt.Errorf("%w: no such id [%s]", taskstorage.ErrNoEnt, id) 145 } 146 return entry.Update(new, func(old_ taskstorage.TaskState) bool { return old_ == old }, r.getNow), nil 147 } 148 149 func updateState(store *syncparam.Map[string, *ent], id string, state taskstorage.TaskState, getNow common.GetNower) (bool, error) { 150 entry, ok := store.Load(id) 151 if !ok { 152 return false, fmt.Errorf("%w: no such id [%s]", taskstorage.ErrNoEnt, id) 153 } 154 return entry.Update(state, isUpdatable, getNow), nil 155 } 156 157 func (r *InMemoryRepo) Update(id string, diff taskstorage.UpdateDiff) error { 158 entry, ok := r.store.Load(id) 159 if !ok { 160 return fmt.Errorf("%w: no such id [%s]", taskstorage.ErrNoEnt, id) 161 } 162 163 if !entry.UpdateByDiff(diff, r.getNow) { 164 return taskstorage.ErrNotUpdatableState 165 } 166 167 return nil 168 } 169 170 func isUpdatable(state taskstorage.TaskState) bool { 171 return !(state == taskstorage.Done || state == taskstorage.Cancelled || state == taskstorage.Failed) 172 }