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.

208 lines
5.3 KiB

5 years ago
5 years ago
5 years ago
5 years ago
  1. package etcd
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "go.etcd.io/etcd/clientv3"
  8. "github.com/chrislusf/seaweedfs/weed/filer"
  9. "github.com/chrislusf/seaweedfs/weed/glog"
  10. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  11. weed_util "github.com/chrislusf/seaweedfs/weed/util"
  12. )
  13. const (
  14. DIR_FILE_SEPARATOR = byte(0x00)
  15. )
  16. func init() {
  17. filer.Stores = append(filer.Stores, &EtcdStore{})
  18. }
  19. type EtcdStore struct {
  20. client *clientv3.Client
  21. }
  22. func (store *EtcdStore) GetName() string {
  23. return "etcd"
  24. }
  25. func (store *EtcdStore) Initialize(configuration weed_util.Configuration, prefix string) (err error) {
  26. servers := configuration.GetString(prefix + "servers")
  27. if servers == "" {
  28. servers = "localhost:2379"
  29. }
  30. timeout := configuration.GetString(prefix + "timeout")
  31. if timeout == "" {
  32. timeout = "3s"
  33. }
  34. return store.initialize(servers, timeout)
  35. }
  36. func (store *EtcdStore) initialize(servers string, timeout string) (err error) {
  37. glog.Infof("filer store etcd: %s", servers)
  38. to, err := time.ParseDuration(timeout)
  39. if err != nil {
  40. return fmt.Errorf("parse timeout %s: %s", timeout, err)
  41. }
  42. store.client, err = clientv3.New(clientv3.Config{
  43. Endpoints: strings.Split(servers, ","),
  44. DialTimeout: to,
  45. })
  46. if err != nil {
  47. return fmt.Errorf("connect to etcd %s: %s", servers, err)
  48. }
  49. return
  50. }
  51. func (store *EtcdStore) BeginTransaction(ctx context.Context) (context.Context, error) {
  52. return ctx, nil
  53. }
  54. func (store *EtcdStore) CommitTransaction(ctx context.Context) error {
  55. return nil
  56. }
  57. func (store *EtcdStore) RollbackTransaction(ctx context.Context) error {
  58. return nil
  59. }
  60. func (store *EtcdStore) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) {
  61. key := genKey(entry.DirAndName())
  62. meta, err := entry.EncodeAttributesAndChunks()
  63. if err != nil {
  64. return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
  65. }
  66. if len(entry.Chunks) > 50 {
  67. meta = weed_util.MaybeGzipData(meta)
  68. }
  69. if _, err := store.client.Put(ctx, string(key), string(meta)); err != nil {
  70. return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
  71. }
  72. return nil
  73. }
  74. func (store *EtcdStore) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) {
  75. return store.InsertEntry(ctx, entry)
  76. }
  77. func (store *EtcdStore) FindEntry(ctx context.Context, fullpath weed_util.FullPath) (entry *filer.Entry, err error) {
  78. key := genKey(fullpath.DirAndName())
  79. resp, err := store.client.Get(ctx, string(key))
  80. if err != nil {
  81. return nil, fmt.Errorf("get %s : %v", entry.FullPath, err)
  82. }
  83. if len(resp.Kvs) == 0 {
  84. return nil, filer_pb.ErrNotFound
  85. }
  86. entry = &filer.Entry{
  87. FullPath: fullpath,
  88. }
  89. err = entry.DecodeAttributesAndChunks(weed_util.MaybeDecompressData(resp.Kvs[0].Value))
  90. if err != nil {
  91. return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
  92. }
  93. return entry, nil
  94. }
  95. func (store *EtcdStore) DeleteEntry(ctx context.Context, fullpath weed_util.FullPath) (err error) {
  96. key := genKey(fullpath.DirAndName())
  97. if _, err := store.client.Delete(ctx, string(key)); err != nil {
  98. return fmt.Errorf("delete %s : %v", fullpath, err)
  99. }
  100. return nil
  101. }
  102. func (store *EtcdStore) DeleteFolderChildren(ctx context.Context, fullpath weed_util.FullPath) (err error) {
  103. directoryPrefix := genDirectoryKeyPrefix(fullpath, "")
  104. if _, err := store.client.Delete(ctx, string(directoryPrefix), clientv3.WithPrefix()); err != nil {
  105. return fmt.Errorf("deleteFolderChildren %s : %v", fullpath, err)
  106. }
  107. return nil
  108. }
  109. func (store *EtcdStore) ListDirectoryPrefixedEntries(ctx context.Context, fullpath weed_util.FullPath, startFileName string, inclusive bool, limit int, prefix string) (entries []*filer.Entry, err error) {
  110. return nil, filer.ErrUnsupportedListDirectoryPrefixed
  111. }
  112. func (store *EtcdStore) ListDirectoryEntries(ctx context.Context, fullpath weed_util.FullPath, startFileName string, inclusive bool, limit int) (entries []*filer.Entry, err error) {
  113. directoryPrefix := genDirectoryKeyPrefix(fullpath, "")
  114. resp, err := store.client.Get(ctx, string(directoryPrefix),
  115. clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortDescend))
  116. if err != nil {
  117. return nil, fmt.Errorf("list %s : %v", fullpath, err)
  118. }
  119. for _, kv := range resp.Kvs {
  120. fileName := getNameFromKey(kv.Key)
  121. if fileName == "" {
  122. continue
  123. }
  124. if fileName == startFileName && !inclusive {
  125. continue
  126. }
  127. limit--
  128. if limit < 0 {
  129. break
  130. }
  131. entry := &filer.Entry{
  132. FullPath: weed_util.NewFullPath(string(fullpath), fileName),
  133. }
  134. if decodeErr := entry.DecodeAttributesAndChunks(weed_util.MaybeDecompressData(kv.Value)); decodeErr != nil {
  135. err = decodeErr
  136. glog.V(0).Infof("list %s : %v", entry.FullPath, err)
  137. break
  138. }
  139. entries = append(entries, entry)
  140. }
  141. return entries, err
  142. }
  143. func genKey(dirPath, fileName string) (key []byte) {
  144. key = []byte(dirPath)
  145. key = append(key, DIR_FILE_SEPARATOR)
  146. key = append(key, []byte(fileName)...)
  147. return key
  148. }
  149. func genDirectoryKeyPrefix(fullpath weed_util.FullPath, startFileName string) (keyPrefix []byte) {
  150. keyPrefix = []byte(string(fullpath))
  151. keyPrefix = append(keyPrefix, DIR_FILE_SEPARATOR)
  152. if len(startFileName) > 0 {
  153. keyPrefix = append(keyPrefix, []byte(startFileName)...)
  154. }
  155. return keyPrefix
  156. }
  157. func getNameFromKey(key []byte) string {
  158. sepIndex := len(key) - 1
  159. for sepIndex >= 0 && key[sepIndex] != DIR_FILE_SEPARATOR {
  160. sepIndex--
  161. }
  162. return string(key[sepIndex+1:])
  163. }
  164. func (store *EtcdStore) Shutdown() {
  165. store.client.Close()
  166. }