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.

360 lines
8.0 KiB

3 years ago
  1. package tikv
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/sha1"
  6. "fmt"
  7. "io"
  8. "strings"
  9. "github.com/chrislusf/seaweedfs/weed/filer"
  10. "github.com/chrislusf/seaweedfs/weed/glog"
  11. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  12. "github.com/chrislusf/seaweedfs/weed/util"
  13. "github.com/tikv/client-go/v2/tikv"
  14. "github.com/tikv/client-go/v2/txnkv"
  15. )
  16. func init() {
  17. filer.Stores = append(filer.Stores, &TikvStore{})
  18. }
  19. type TikvStore struct {
  20. client *tikv.KVStore
  21. }
  22. // Basic APIs
  23. func (store *TikvStore) GetName() string {
  24. return "tikv"
  25. }
  26. func (store *TikvStore) Initialize(config util.Configuration, prefix string) error {
  27. pdAddrs := []string{}
  28. pdAddrsStr := config.GetString(prefix + "pdaddrs")
  29. for _, item := range strings.Split(pdAddrsStr, ",") {
  30. pdAddrs = append(pdAddrs, strings.TrimSpace(item))
  31. }
  32. return store.initialize(pdAddrs)
  33. }
  34. func (store *TikvStore) initialize(pdAddrs []string) error {
  35. client, err := tikv.NewTxnClient(pdAddrs)
  36. store.client = client
  37. return err
  38. }
  39. func (store *TikvStore) Shutdown() {
  40. err := store.client.Close()
  41. if err != nil {
  42. glog.V(0).Infof("Shutdown TiKV client got error: %v", err)
  43. }
  44. }
  45. // ~ Basic APIs
  46. // Entry APIs
  47. func (store *TikvStore) InsertEntry(ctx context.Context, entry *filer.Entry) error {
  48. dir, name := entry.DirAndName()
  49. key := generateKey(dir, name)
  50. value, err := entry.EncodeAttributesAndChunks()
  51. if err != nil {
  52. return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
  53. }
  54. txn, err := store.getTxn(ctx)
  55. if err != nil {
  56. return err
  57. }
  58. err = txn.RunInTxn(func(txn *txnkv.KVTxn) error {
  59. return txn.Set(key, value)
  60. })
  61. if err != nil {
  62. return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
  63. }
  64. return nil
  65. }
  66. func (store *TikvStore) UpdateEntry(ctx context.Context, entry *filer.Entry) error {
  67. return store.InsertEntry(ctx, entry)
  68. }
  69. func (store *TikvStore) FindEntry(ctx context.Context, path util.FullPath) (*filer.Entry, error) {
  70. dir, name := path.DirAndName()
  71. key := generateKey(dir, name)
  72. txn, err := store.getTxn(ctx)
  73. if err != nil {
  74. return nil, err
  75. }
  76. var value []byte = nil
  77. err = txn.RunInTxn(func(txn *txnkv.KVTxn) error {
  78. val, err := txn.Get(context.TODO(), key)
  79. if err == nil {
  80. value = val
  81. }
  82. return err
  83. })
  84. if isNotExists(err) || value == nil {
  85. return nil, filer_pb.ErrNotFound
  86. }
  87. if err != nil {
  88. return nil, fmt.Errorf("get %s : %v", path, err)
  89. }
  90. entry := &filer.Entry{
  91. FullPath: path,
  92. }
  93. err = entry.DecodeAttributesAndChunks(value)
  94. if err != nil {
  95. return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
  96. }
  97. return entry, nil
  98. }
  99. func (store *TikvStore) DeleteEntry(ctx context.Context, path util.FullPath) error {
  100. dir, name := path.DirAndName()
  101. key := generateKey(dir, name)
  102. txn, err := store.getTxn(ctx)
  103. if err != nil {
  104. return err
  105. }
  106. err = txn.RunInTxn(func(txn *txnkv.KVTxn) error {
  107. return txn.Delete(key)
  108. })
  109. if err != nil {
  110. return fmt.Errorf("delete %s : %v", path, err)
  111. }
  112. return nil
  113. }
  114. // ~ Entry APIs
  115. // Directory APIs
  116. func (store *TikvStore) DeleteFolderChildren(ctx context.Context, path util.FullPath) error {
  117. directoryPrefix := genDirectoryKeyPrefix(path, "")
  118. txn, err := store.getTxn(ctx)
  119. if err != nil {
  120. return err
  121. }
  122. err = txn.RunInTxn(func(txn *txnkv.KVTxn) error {
  123. iter, err := txn.Iter(directoryPrefix, nil)
  124. if err != nil {
  125. return err
  126. }
  127. defer iter.Close()
  128. for iter.Valid() {
  129. key := iter.Key()
  130. if !bytes.HasPrefix(key, directoryPrefix) {
  131. break
  132. }
  133. err = txn.Delete(key)
  134. if err != nil {
  135. return err
  136. }
  137. err = iter.Next()
  138. if err != nil {
  139. return err
  140. }
  141. }
  142. return nil
  143. })
  144. if err != nil {
  145. return fmt.Errorf("delete %s : %v", path, err)
  146. }
  147. return nil
  148. }
  149. func (store *TikvStore) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (string, error) {
  150. return store.ListDirectoryPrefixedEntries(ctx, dirPath, startFileName, includeStartFile, limit, "", eachEntryFunc)
  151. }
  152. func (store *TikvStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (string, error) {
  153. lastFileName := ""
  154. directoryPrefix := genDirectoryKeyPrefix(dirPath, prefix)
  155. lastFileStart := directoryPrefix
  156. if startFileName != "" {
  157. lastFileStart = genDirectoryKeyPrefix(dirPath, startFileName)
  158. }
  159. txn, err := store.getTxn(ctx)
  160. if err != nil {
  161. return lastFileName, err
  162. }
  163. err = txn.RunInTxn(func(txn *txnkv.KVTxn) error {
  164. iter, err := txn.Iter(lastFileStart, nil)
  165. if err != nil {
  166. return err
  167. }
  168. defer iter.Close()
  169. i := int64(0)
  170. first := true
  171. for iter.Valid() {
  172. if first {
  173. first = false
  174. if !includeStartFile {
  175. if iter.Valid() {
  176. // Check first item is lastFileStart
  177. if bytes.Equal(iter.Key(), lastFileStart) {
  178. // Is lastFileStart and not include start file, just
  179. // ignore it.
  180. err = iter.Next()
  181. if err != nil {
  182. return err
  183. }
  184. continue
  185. }
  186. }
  187. }
  188. }
  189. // Check for limitation
  190. if limit > 0 {
  191. i++
  192. if i > limit {
  193. break
  194. }
  195. }
  196. // Validate key prefix
  197. key := iter.Key()
  198. if !bytes.HasPrefix(key, directoryPrefix) {
  199. break
  200. }
  201. value := iter.Value()
  202. // Start process
  203. fileName := getNameFromKey(key)
  204. if fileName != "" {
  205. // Got file name, then generate the Entry
  206. entry := &filer.Entry{
  207. FullPath: util.NewFullPath(string(dirPath), fileName),
  208. }
  209. // Update lastFileName
  210. lastFileName = fileName
  211. // Check for decode value.
  212. if decodeErr := entry.DecodeAttributesAndChunks(value); decodeErr != nil {
  213. // Got error just return the error
  214. glog.V(0).Infof("list %s : %v", entry.FullPath, err)
  215. return err
  216. }
  217. // Run for each callback if return false just break the iteration
  218. if !eachEntryFunc(entry) {
  219. break
  220. }
  221. }
  222. // End process
  223. err = iter.Next()
  224. if err != nil {
  225. return err
  226. }
  227. }
  228. return nil
  229. })
  230. if err != nil {
  231. return lastFileName, fmt.Errorf("prefix list %s : %v", dirPath, err)
  232. }
  233. return lastFileName, nil
  234. }
  235. // ~ Directory APIs
  236. // Transaction Related APIs
  237. func (store *TikvStore) BeginTransaction(ctx context.Context) (context.Context, error) {
  238. tx, err := store.client.Begin()
  239. if err != nil {
  240. return ctx, err
  241. }
  242. return context.WithValue(ctx, "tx", tx), nil
  243. }
  244. func (store *TikvStore) CommitTransaction(ctx context.Context) error {
  245. if tx, ok := ctx.Value("tx").(*txnkv.KVTxn); ok {
  246. return tx.Commit(context.Background())
  247. }
  248. return nil
  249. }
  250. func (store *TikvStore) RollbackTransaction(ctx context.Context) error {
  251. if tx, ok := ctx.Value("tx").(*txnkv.KVTxn); ok {
  252. return tx.Rollback()
  253. }
  254. return nil
  255. }
  256. // ~ Transaction Related APIs
  257. // Transaction Wrapper
  258. type TxnWrapper struct {
  259. *txnkv.KVTxn
  260. inContext bool
  261. }
  262. func (w *TxnWrapper) RunInTxn(f func(txn *txnkv.KVTxn) error) error {
  263. err := f(w.KVTxn)
  264. if !w.inContext {
  265. if err != nil {
  266. w.KVTxn.Rollback()
  267. return err
  268. }
  269. w.KVTxn.Commit(context.Background())
  270. return nil
  271. }
  272. return err
  273. }
  274. func (store *TikvStore) getTxn(ctx context.Context) (*TxnWrapper, error) {
  275. if tx, ok := ctx.Value("tx").(*txnkv.KVTxn); ok {
  276. return &TxnWrapper{tx, true}, nil
  277. }
  278. txn, err := store.client.Begin()
  279. if err != nil {
  280. return nil, err
  281. }
  282. return &TxnWrapper{txn, false}, nil
  283. }
  284. // ~ Transaction Wrapper
  285. // Encoding Functions
  286. func hashToBytes(dir string) []byte {
  287. h := sha1.New()
  288. io.WriteString(h, dir)
  289. b := h.Sum(nil)
  290. return b
  291. }
  292. func generateKey(dirPath, fileName string) []byte {
  293. key := hashToBytes(dirPath)
  294. key = append(key, []byte(fileName)...)
  295. return key
  296. }
  297. func getNameFromKey(key []byte) string {
  298. return string(key[sha1.Size:])
  299. }
  300. func genDirectoryKeyPrefix(fullpath util.FullPath, startFileName string) (keyPrefix []byte) {
  301. keyPrefix = hashToBytes(string(fullpath))
  302. if len(startFileName) > 0 {
  303. keyPrefix = append(keyPrefix, []byte(startFileName)...)
  304. }
  305. return keyPrefix
  306. }
  307. func isNotExists(err error) bool {
  308. if err == nil {
  309. return false
  310. }
  311. if err.Error() == "not exist" {
  312. return true
  313. }
  314. return false
  315. }
  316. // ~ Encoding Functions