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.

206 lines
5.8 KiB

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