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  }