store.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package mem
  2. import (
  3. "context"
  4. "strings"
  5. "sync"
  6. "time"
  7. "github.com/hack-pad/hackpadfs"
  8. "github.com/hack-pad/hackpadfs/keyvalue"
  9. "github.com/hack-pad/hackpadfs/keyvalue/blob"
  10. )
  11. var _ keyvalue.TransactionStore = &store{}
  12. type store struct {
  13. records sync.Map
  14. mu sync.Mutex
  15. }
  16. func newStore() *store {
  17. return &store{}
  18. }
  19. type fileRecord struct {
  20. data blob.Blob
  21. modTime time.Time
  22. store *store
  23. path string
  24. mode hackpadfs.FileMode
  25. }
  26. func (f fileRecord) Data() (blob.Blob, error) {
  27. return f.data, nil
  28. }
  29. func (f fileRecord) Size() int64 { return int64(f.data.Len()) }
  30. func (f fileRecord) Mode() hackpadfs.FileMode { return f.mode }
  31. func (f fileRecord) ModTime() time.Time { return f.modTime }
  32. func (f fileRecord) Sys() interface{} { return nil }
  33. func (f fileRecord) ReadDirNames() ([]string, error) {
  34. if !f.mode.IsDir() {
  35. return nil, hackpadfs.ErrNotDir
  36. }
  37. var names []string
  38. prefix := f.path + "/"
  39. isRoot := f.path == "."
  40. if isRoot {
  41. prefix = ""
  42. }
  43. f.store.records.Range(func(key, _ interface{}) bool {
  44. p := key.(string)
  45. if strings.HasPrefix(p, prefix) {
  46. p = strings.TrimPrefix(p, prefix)
  47. if !strings.ContainsRune(p, '/') && !(isRoot && p == ".") {
  48. names = append(names, p)
  49. }
  50. }
  51. return true
  52. })
  53. return names, nil
  54. }
  55. func (s *store) Get(_ context.Context, path string) (keyvalue.FileRecord, error) {
  56. value, ok := s.records.Load(path)
  57. if !ok {
  58. return nil, hackpadfs.ErrNotExist
  59. }
  60. record := value.(keyvalue.FileRecord)
  61. return record, nil
  62. }
  63. func (s *store) Set(_ context.Context, path string, src keyvalue.FileRecord) error {
  64. var contents blob.Blob
  65. if src != nil {
  66. var err error
  67. contents, err = src.Data()
  68. if err != nil {
  69. return err
  70. }
  71. }
  72. return s.set(path, src, contents)
  73. }
  74. func (s *store) set(path string, src keyvalue.FileRecord, _ blob.Blob) error {
  75. if src == nil {
  76. s.records.Delete(path)
  77. } else {
  78. data, err := src.Data()
  79. if err != nil {
  80. return err
  81. }
  82. record := fileRecord{
  83. store: s,
  84. path: path,
  85. data: data,
  86. mode: src.Mode(),
  87. modTime: src.ModTime(),
  88. }
  89. s.records.Store(path, record)
  90. }
  91. return nil
  92. }
  93. type transaction struct {
  94. ctx context.Context
  95. abort context.CancelFunc
  96. store *store
  97. results []keyvalue.OpResult
  98. op keyvalue.OpID
  99. }
  100. func (s *store) Transaction(_ keyvalue.TransactionOptions) (keyvalue.Transaction, error) {
  101. ctx, cancel := context.WithCancel(context.Background())
  102. txn := &transaction{
  103. ctx: ctx,
  104. abort: cancel,
  105. store: s,
  106. }
  107. s.mu.Lock()
  108. return txn, nil
  109. }
  110. func (t *transaction) prepOp() (keyvalue.OpID, error) {
  111. select {
  112. case <-t.ctx.Done():
  113. return 0, t.ctx.Err()
  114. default:
  115. }
  116. op := t.op
  117. t.op++
  118. return op, nil
  119. }
  120. func (t *transaction) Get(path string) keyvalue.OpID {
  121. return t.GetHandler(path, keyvalue.OpHandlerFunc(func(_ keyvalue.Transaction, _ keyvalue.OpResult) error {
  122. return nil
  123. }))
  124. }
  125. func (t *transaction) GetHandler(path string, handler keyvalue.OpHandler) keyvalue.OpID {
  126. op, err := t.prepOp()
  127. if err != nil {
  128. t.results = append(t.results, keyvalue.OpResult{Op: op, Err: err})
  129. return op
  130. }
  131. record, err := t.store.Get(t.ctx, path)
  132. result := keyvalue.OpResult{Op: op, Record: record, Err: err}
  133. err = handler.Handle(t, result)
  134. if result.Err == nil && err != nil {
  135. result.Err = err
  136. }
  137. t.results = append(t.results, result)
  138. return op
  139. }
  140. func (t *transaction) Set(path string, src keyvalue.FileRecord, contents blob.Blob) keyvalue.OpID {
  141. return t.SetHandler(path, src, contents, keyvalue.OpHandlerFunc(func(_ keyvalue.Transaction, _ keyvalue.OpResult) error {
  142. return nil
  143. }))
  144. }
  145. func (t *transaction) SetHandler(path string, src keyvalue.FileRecord, contents blob.Blob, handler keyvalue.OpHandler) keyvalue.OpID {
  146. op, err := t.prepOp()
  147. if err != nil {
  148. t.results = append(t.results, keyvalue.OpResult{Op: op, Err: err})
  149. return op
  150. }
  151. err = t.store.set(path, src, contents)
  152. result := keyvalue.OpResult{Op: op, Err: err}
  153. err = handler.Handle(t, result)
  154. if result.Err == nil && err != nil {
  155. result.Err = err
  156. }
  157. t.results = append(t.results, result)
  158. return op
  159. }
  160. func (t *transaction) Commit(_ context.Context) ([]keyvalue.OpResult, error) {
  161. t.abort()
  162. t.store.mu.Unlock()
  163. return t.results, nil
  164. }
  165. func (t *transaction) Abort() error {
  166. t.abort()
  167. t.store.mu.Unlock()
  168. return nil
  169. }