You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

217 lines
5.9 KiB

4 years ago
3 years ago
  1. package redis2
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "github.com/go-redis/redis/v8"
  7. "github.com/seaweedfs/seaweedfs/weed/filer"
  8. "github.com/seaweedfs/seaweedfs/weed/glog"
  9. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  10. "github.com/seaweedfs/seaweedfs/weed/util"
  11. )
  12. const (
  13. DIR_LIST_MARKER = "\x00"
  14. )
  15. type UniversalRedis2Store struct {
  16. Client redis.UniversalClient
  17. superLargeDirectoryHash map[string]bool
  18. }
  19. func (store *UniversalRedis2Store) isSuperLargeDirectory(dir string) (isSuperLargeDirectory bool) {
  20. _, isSuperLargeDirectory = store.superLargeDirectoryHash[dir]
  21. return
  22. }
  23. func (store *UniversalRedis2Store) loadSuperLargeDirectories(superLargeDirectories []string) {
  24. // set directory hash
  25. store.superLargeDirectoryHash = make(map[string]bool)
  26. for _, dir := range superLargeDirectories {
  27. store.superLargeDirectoryHash[dir] = true
  28. }
  29. }
  30. func (store *UniversalRedis2Store) BeginTransaction(ctx context.Context) (context.Context, error) {
  31. return ctx, nil
  32. }
  33. func (store *UniversalRedis2Store) CommitTransaction(ctx context.Context) error {
  34. return nil
  35. }
  36. func (store *UniversalRedis2Store) RollbackTransaction(ctx context.Context) error {
  37. return nil
  38. }
  39. func (store *UniversalRedis2Store) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) {
  40. value, err := entry.EncodeAttributesAndChunks()
  41. if err != nil {
  42. return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
  43. }
  44. if len(entry.Chunks) > filer.CountEntryChunksForGzip {
  45. value = util.MaybeGzipData(value)
  46. }
  47. if err = store.Client.Set(ctx, string(entry.FullPath), value, time.Duration(entry.TtlSec)*time.Second).Err(); err != nil {
  48. return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
  49. }
  50. dir, name := entry.FullPath.DirAndName()
  51. if store.isSuperLargeDirectory(dir) {
  52. return nil
  53. }
  54. if name != "" {
  55. if err = store.Client.ZAddNX(ctx, genDirectoryListKey(dir), &redis.Z{Score: 0, Member: name}).Err(); err != nil {
  56. return fmt.Errorf("persisting %s in parent dir: %v", entry.FullPath, err)
  57. }
  58. }
  59. return nil
  60. }
  61. func (store *UniversalRedis2Store) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) {
  62. return store.InsertEntry(ctx, entry)
  63. }
  64. func (store *UniversalRedis2Store) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) {
  65. data, err := store.Client.Get(ctx, string(fullpath)).Result()
  66. if err == redis.Nil {
  67. return nil, filer_pb.ErrNotFound
  68. }
  69. if err != nil {
  70. return nil, fmt.Errorf("get %s : %v", fullpath, err)
  71. }
  72. entry = &filer.Entry{
  73. FullPath: fullpath,
  74. }
  75. err = entry.DecodeAttributesAndChunks(util.MaybeDecompressData([]byte(data)))
  76. if err != nil {
  77. return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
  78. }
  79. return entry, nil
  80. }
  81. func (store *UniversalRedis2Store) DeleteEntry(ctx context.Context, fullpath util.FullPath) (err error) {
  82. _, err = store.Client.Del(ctx, genDirectoryListKey(string(fullpath))).Result()
  83. if err != nil {
  84. return fmt.Errorf("delete dir list %s : %v", fullpath, err)
  85. }
  86. _, err = store.Client.Del(ctx, string(fullpath)).Result()
  87. if err != nil {
  88. return fmt.Errorf("delete %s : %v", fullpath, err)
  89. }
  90. dir, name := fullpath.DirAndName()
  91. if store.isSuperLargeDirectory(dir) {
  92. return nil
  93. }
  94. if name != "" {
  95. _, err = store.Client.ZRem(ctx, genDirectoryListKey(dir), name).Result()
  96. if err != nil {
  97. return fmt.Errorf("DeleteEntry %s in parent dir: %v", fullpath, err)
  98. }
  99. }
  100. return nil
  101. }
  102. func (store *UniversalRedis2Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) {
  103. if store.isSuperLargeDirectory(string(fullpath)) {
  104. return nil
  105. }
  106. members, err := store.Client.ZRangeByLex(ctx, genDirectoryListKey(string(fullpath)), &redis.ZRangeBy{
  107. Min: "-",
  108. Max: "+",
  109. }).Result()
  110. if err != nil {
  111. return fmt.Errorf("DeleteFolderChildren %s : %v", fullpath, err)
  112. }
  113. for _, fileName := range members {
  114. path := util.NewFullPath(string(fullpath), fileName)
  115. _, err = store.Client.Del(ctx, string(path)).Result()
  116. if err != nil {
  117. return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err)
  118. }
  119. // not efficient, but need to remove if it is a directory
  120. store.Client.Del(ctx, genDirectoryListKey(string(path)))
  121. }
  122. return nil
  123. }
  124. func (store *UniversalRedis2Store) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
  125. return lastFileName, filer.ErrUnsupportedListDirectoryPrefixed
  126. }
  127. func (store *UniversalRedis2Store) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
  128. dirListKey := genDirectoryListKey(string(dirPath))
  129. min := "-"
  130. if startFileName != "" {
  131. if includeStartFile {
  132. min = "[" + startFileName
  133. } else {
  134. min = "(" + startFileName
  135. }
  136. }
  137. members, err := store.Client.ZRangeByLex(ctx, dirListKey, &redis.ZRangeBy{
  138. Min: min,
  139. Max: "+",
  140. Offset: 0,
  141. Count: limit,
  142. }).Result()
  143. if err != nil {
  144. return lastFileName, fmt.Errorf("list %s : %v", dirPath, err)
  145. }
  146. // fetch entry meta
  147. for _, fileName := range members {
  148. path := util.NewFullPath(string(dirPath), fileName)
  149. entry, err := store.FindEntry(ctx, path)
  150. lastFileName = fileName
  151. if err != nil {
  152. glog.V(0).Infof("list %s : %v", path, err)
  153. if err == filer_pb.ErrNotFound {
  154. continue
  155. }
  156. } else {
  157. if entry.TtlSec > 0 {
  158. if entry.Attr.Crtime.Add(time.Duration(entry.TtlSec) * time.Second).Before(time.Now()) {
  159. store.Client.Del(ctx, string(path)).Result()
  160. store.Client.ZRem(ctx, dirListKey, fileName).Result()
  161. continue
  162. }
  163. }
  164. if !eachEntryFunc(entry) {
  165. break
  166. }
  167. }
  168. }
  169. return lastFileName, err
  170. }
  171. func genDirectoryListKey(dir string) (dirList string) {
  172. return dir + DIR_LIST_MARKER
  173. }
  174. func (store *UniversalRedis2Store) Shutdown() {
  175. store.Client.Close()
  176. }