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.

202 lines
5.0 KiB

5 years ago
5 years ago
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/filer2"
  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. filer2.Stores = append(filer2.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 *filer2.Entry) (err error) {
  61. key := genKey(entry.DirAndName())
  62. value, err := entry.EncodeAttributesAndChunks()
  63. if err != nil {
  64. return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
  65. }
  66. if _, err := store.client.Put(ctx, string(key), string(value)); err != nil {
  67. return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
  68. }
  69. return nil
  70. }
  71. func (store *EtcdStore) UpdateEntry(ctx context.Context, entry *filer2.Entry) (err error) {
  72. return store.InsertEntry(ctx, entry)
  73. }
  74. func (store *EtcdStore) FindEntry(ctx context.Context, fullpath weed_util.FullPath) (entry *filer2.Entry, err error) {
  75. key := genKey(fullpath.DirAndName())
  76. resp, err := store.client.Get(ctx, string(key))
  77. if err != nil {
  78. return nil, fmt.Errorf("get %s : %v", entry.FullPath, err)
  79. }
  80. if len(resp.Kvs) == 0 {
  81. return nil, filer_pb.ErrNotFound
  82. }
  83. entry = &filer2.Entry{
  84. FullPath: fullpath,
  85. }
  86. err = entry.DecodeAttributesAndChunks(resp.Kvs[0].Value)
  87. if err != nil {
  88. return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
  89. }
  90. return entry, nil
  91. }
  92. func (store *EtcdStore) DeleteEntry(ctx context.Context, fullpath weed_util.FullPath) (err error) {
  93. key := genKey(fullpath.DirAndName())
  94. if _, err := store.client.Delete(ctx, string(key)); err != nil {
  95. return fmt.Errorf("delete %s : %v", fullpath, err)
  96. }
  97. return nil
  98. }
  99. func (store *EtcdStore) DeleteFolderChildren(ctx context.Context, fullpath weed_util.FullPath) (err error) {
  100. directoryPrefix := genDirectoryKeyPrefix(fullpath, "")
  101. if _, err := store.client.Delete(ctx, string(directoryPrefix), clientv3.WithPrefix()); err != nil {
  102. return fmt.Errorf("deleteFolderChildren %s : %v", fullpath, err)
  103. }
  104. return nil
  105. }
  106. func (store *EtcdStore) ListDirectoryEntries(
  107. ctx context.Context, fullpath weed_util.FullPath, startFileName string, inclusive bool, limit int,
  108. ) (entries []*filer2.Entry, err error) {
  109. directoryPrefix := genDirectoryKeyPrefix(fullpath, "")
  110. resp, err := store.client.Get(ctx, string(directoryPrefix),
  111. clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortDescend))
  112. if err != nil {
  113. return nil, fmt.Errorf("list %s : %v", fullpath, err)
  114. }
  115. for _, kv := range resp.Kvs {
  116. fileName := getNameFromKey(kv.Key)
  117. if fileName == "" {
  118. continue
  119. }
  120. if fileName == startFileName && !inclusive {
  121. continue
  122. }
  123. limit--
  124. if limit < 0 {
  125. break
  126. }
  127. entry := &filer2.Entry{
  128. FullPath: weed_util.NewFullPath(string(fullpath), fileName),
  129. }
  130. if decodeErr := entry.DecodeAttributesAndChunks(kv.Value); decodeErr != nil {
  131. err = decodeErr
  132. glog.V(0).Infof("list %s : %v", entry.FullPath, err)
  133. break
  134. }
  135. entries = append(entries, entry)
  136. }
  137. return entries, err
  138. }
  139. func genKey(dirPath, fileName string) (key []byte) {
  140. key = []byte(dirPath)
  141. key = append(key, DIR_FILE_SEPARATOR)
  142. key = append(key, []byte(fileName)...)
  143. return key
  144. }
  145. func genDirectoryKeyPrefix(fullpath weed_util.FullPath, startFileName string) (keyPrefix []byte) {
  146. keyPrefix = []byte(string(fullpath))
  147. keyPrefix = append(keyPrefix, DIR_FILE_SEPARATOR)
  148. if len(startFileName) > 0 {
  149. keyPrefix = append(keyPrefix, []byte(startFileName)...)
  150. }
  151. return keyPrefix
  152. }
  153. func getNameFromKey(key []byte) string {
  154. sepIndex := len(key) - 1
  155. for sepIndex >= 0 && key[sepIndex] != DIR_FILE_SEPARATOR {
  156. sepIndex--
  157. }
  158. return string(key[sepIndex+1:])
  159. }
  160. func (store *EtcdStore) Shutdown() {
  161. store.client.Close()
  162. }