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.

256 lines
7.8 KiB

6 years ago
6 years ago
4 years ago
4 years ago
4 years ago
6 years ago
4 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
  1. package filersink
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/seaweedfs/seaweedfs/weed/pb"
  6. "github.com/seaweedfs/seaweedfs/weed/wdclient"
  7. "math"
  8. "google.golang.org/grpc"
  9. "github.com/seaweedfs/seaweedfs/weed/security"
  10. "github.com/seaweedfs/seaweedfs/weed/filer"
  11. "github.com/seaweedfs/seaweedfs/weed/glog"
  12. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  13. "github.com/seaweedfs/seaweedfs/weed/replication/sink"
  14. "github.com/seaweedfs/seaweedfs/weed/replication/source"
  15. "github.com/seaweedfs/seaweedfs/weed/util"
  16. )
  17. type FilerSink struct {
  18. filerSource *source.FilerSource
  19. grpcAddress string
  20. dir string
  21. replication string
  22. collection string
  23. ttlSec int32
  24. diskType string
  25. dataCenter string
  26. grpcDialOption grpc.DialOption
  27. address string
  28. writeChunkByFiler bool
  29. isIncremental bool
  30. executor *util.LimitedConcurrentExecutor
  31. }
  32. func init() {
  33. sink.Sinks = append(sink.Sinks, &FilerSink{})
  34. }
  35. func (fs *FilerSink) GetName() string {
  36. return "filer"
  37. }
  38. func (fs *FilerSink) GetSinkToDirectory() string {
  39. return fs.dir
  40. }
  41. func (fs *FilerSink) IsIncremental() bool {
  42. return fs.isIncremental
  43. }
  44. func (fs *FilerSink) Initialize(configuration util.Configuration, prefix string) error {
  45. fs.isIncremental = configuration.GetBool(prefix + "is_incremental")
  46. fs.dataCenter = configuration.GetString(prefix + "dataCenter")
  47. return fs.DoInitialize(
  48. "",
  49. configuration.GetString(prefix+"grpcAddress"),
  50. configuration.GetString(prefix+"directory"),
  51. configuration.GetString(prefix+"replication"),
  52. configuration.GetString(prefix+"collection"),
  53. configuration.GetInt(prefix+"ttlSec"),
  54. configuration.GetString(prefix+"disk"),
  55. security.LoadClientTLS(util.GetViper(), "grpc.client"),
  56. false)
  57. }
  58. func (fs *FilerSink) SetSourceFiler(s *source.FilerSource) {
  59. fs.filerSource = s
  60. }
  61. func (fs *FilerSink) DoInitialize(address, grpcAddress string, dir string,
  62. replication string, collection string, ttlSec int, diskType string, grpcDialOption grpc.DialOption, writeChunkByFiler bool) (err error) {
  63. fs.address = address
  64. if fs.address == "" {
  65. fs.address = pb.GrpcAddressToServerAddress(grpcAddress)
  66. }
  67. fs.grpcAddress = grpcAddress
  68. fs.dir = dir
  69. fs.replication = replication
  70. fs.collection = collection
  71. fs.ttlSec = int32(ttlSec)
  72. fs.diskType = diskType
  73. fs.grpcDialOption = grpcDialOption
  74. fs.writeChunkByFiler = writeChunkByFiler
  75. fs.executor = util.NewLimitedConcurrentExecutor(32)
  76. return nil
  77. }
  78. func (fs *FilerSink) DeleteEntry(key string, isDirectory, deleteIncludeChunks bool, signatures []int32) error {
  79. dir, name := util.FullPath(key).DirAndName()
  80. glog.V(4).Infof("delete entry: %v", key)
  81. err := filer_pb.Remove(fs, dir, name, deleteIncludeChunks, true, true, true, signatures)
  82. if err != nil {
  83. glog.V(0).Infof("delete entry %s: %v", key, err)
  84. return fmt.Errorf("delete entry %s: %v", key, err)
  85. }
  86. return nil
  87. }
  88. func (fs *FilerSink) CreateEntry(key string, entry *filer_pb.Entry, signatures []int32) error {
  89. return fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  90. dir, name := util.FullPath(key).DirAndName()
  91. // look up existing entry
  92. lookupRequest := &filer_pb.LookupDirectoryEntryRequest{
  93. Directory: dir,
  94. Name: name,
  95. }
  96. // glog.V(1).Infof("lookup: %v", lookupRequest)
  97. if resp, err := filer_pb.LookupEntry(client, lookupRequest); err == nil {
  98. if filer.ETag(resp.Entry) == filer.ETag(entry) {
  99. glog.V(3).Infof("already replicated %s", key)
  100. return nil
  101. }
  102. }
  103. replicatedChunks, err := fs.replicateChunks(entry.GetChunks(), key)
  104. if err != nil {
  105. // only warning here since the source chunk may have been deleted already
  106. glog.Warningf("replicate entry chunks %s: %v", key, err)
  107. return nil
  108. }
  109. // glog.V(4).Infof("replicated %s %+v ===> %+v", key, entry.GetChunks(), replicatedChunks)
  110. request := &filer_pb.CreateEntryRequest{
  111. Directory: dir,
  112. Entry: &filer_pb.Entry{
  113. Name: name,
  114. IsDirectory: entry.IsDirectory,
  115. Attributes: entry.Attributes,
  116. Extended: entry.Extended,
  117. Chunks: replicatedChunks,
  118. Content: entry.Content,
  119. RemoteEntry: entry.RemoteEntry,
  120. },
  121. IsFromOtherCluster: true,
  122. Signatures: signatures,
  123. }
  124. glog.V(3).Infof("create: %v", request)
  125. if err := filer_pb.CreateEntry(client, request); err != nil {
  126. glog.V(0).Infof("create entry %s: %v", key, err)
  127. return fmt.Errorf("create entry %s: %v", key, err)
  128. }
  129. return nil
  130. })
  131. }
  132. func (fs *FilerSink) UpdateEntry(key string, oldEntry *filer_pb.Entry, newParentPath string, newEntry *filer_pb.Entry, deleteIncludeChunks bool, signatures []int32) (foundExistingEntry bool, err error) {
  133. dir, name := util.FullPath(key).DirAndName()
  134. // read existing entry
  135. var existingEntry *filer_pb.Entry
  136. err = fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  137. request := &filer_pb.LookupDirectoryEntryRequest{
  138. Directory: dir,
  139. Name: name,
  140. }
  141. glog.V(4).Infof("lookup entry: %v", request)
  142. resp, err := filer_pb.LookupEntry(client, request)
  143. if err != nil {
  144. glog.V(0).Infof("lookup %s: %v", key, err)
  145. return err
  146. }
  147. existingEntry = resp.Entry
  148. return nil
  149. })
  150. if err != nil {
  151. return false, fmt.Errorf("lookup %s: %v", key, err)
  152. }
  153. glog.V(4).Infof("oldEntry %+v, newEntry %+v, existingEntry: %+v", oldEntry, newEntry, existingEntry)
  154. if existingEntry.Attributes.Mtime > newEntry.Attributes.Mtime {
  155. // skip if already changed
  156. // this usually happens when the messages are not ordered
  157. glog.V(2).Infof("late updates %s", key)
  158. } else {
  159. // find out what changed
  160. deletedChunks, newChunks, err := compareChunks(filer.LookupFn(fs), oldEntry, newEntry)
  161. if err != nil {
  162. return true, fmt.Errorf("replicate %s compare chunks error: %v", key, err)
  163. }
  164. // delete the chunks that are deleted from the source
  165. if deleteIncludeChunks {
  166. // remove the deleted chunks. Actual data deletion happens in filer UpdateEntry FindUnusedFileChunks
  167. existingEntry.Chunks = filer.DoMinusChunksBySourceFileId(existingEntry.GetChunks(), deletedChunks)
  168. }
  169. // replicate the chunks that are new in the source
  170. replicatedChunks, err := fs.replicateChunks(newChunks, key)
  171. if err != nil {
  172. glog.Warningf("replicate entry chunks %s: %v", key, err)
  173. return true, nil
  174. }
  175. existingEntry.Chunks = append(existingEntry.GetChunks(), replicatedChunks...)
  176. existingEntry.Attributes = newEntry.Attributes
  177. existingEntry.Extended = newEntry.Extended
  178. existingEntry.HardLinkId = newEntry.HardLinkId
  179. existingEntry.HardLinkCounter = newEntry.HardLinkCounter
  180. existingEntry.Content = newEntry.Content
  181. existingEntry.RemoteEntry = newEntry.RemoteEntry
  182. }
  183. // save updated meta data
  184. return true, fs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  185. request := &filer_pb.UpdateEntryRequest{
  186. Directory: newParentPath,
  187. Entry: existingEntry,
  188. IsFromOtherCluster: true,
  189. Signatures: signatures,
  190. }
  191. if _, err := client.UpdateEntry(context.Background(), request); err != nil {
  192. return fmt.Errorf("update existingEntry %s: %v", key, err)
  193. }
  194. return nil
  195. })
  196. }
  197. func compareChunks(lookupFileIdFn wdclient.LookupFileIdFunctionType, oldEntry, newEntry *filer_pb.Entry) (deletedChunks, newChunks []*filer_pb.FileChunk, err error) {
  198. aData, aMeta, aErr := filer.ResolveChunkManifest(lookupFileIdFn, oldEntry.GetChunks(), 0, math.MaxInt64)
  199. if aErr != nil {
  200. return nil, nil, aErr
  201. }
  202. bData, bMeta, bErr := filer.ResolveChunkManifest(lookupFileIdFn, newEntry.GetChunks(), 0, math.MaxInt64)
  203. if bErr != nil {
  204. return nil, nil, bErr
  205. }
  206. deletedChunks = append(deletedChunks, filer.DoMinusChunks(aData, bData)...)
  207. deletedChunks = append(deletedChunks, filer.DoMinusChunks(aMeta, bMeta)...)
  208. newChunks = append(newChunks, filer.DoMinusChunks(bData, aData)...)
  209. newChunks = append(newChunks, filer.DoMinusChunks(bMeta, aMeta)...)
  210. return
  211. }