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.

249 lines
9.4 KiB

3 years ago
3 years ago
3 years ago
  1. package command
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "time"
  8. "github.com/seaweedfs/seaweedfs/weed/filer"
  9. "github.com/seaweedfs/seaweedfs/weed/glog"
  10. "github.com/seaweedfs/seaweedfs/weed/pb"
  11. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  12. "github.com/seaweedfs/seaweedfs/weed/pb/remote_pb"
  13. "github.com/seaweedfs/seaweedfs/weed/remote_storage"
  14. "github.com/seaweedfs/seaweedfs/weed/replication/source"
  15. "github.com/seaweedfs/seaweedfs/weed/util"
  16. "google.golang.org/grpc"
  17. "google.golang.org/protobuf/proto"
  18. )
  19. func followUpdatesAndUploadToRemote(option *RemoteSyncOptions, filerSource *source.FilerSource, mountedDir string) error {
  20. // read filer remote storage mount mappings
  21. _, _, remoteStorageMountLocation, remoteStorage, detectErr := filer.DetectMountInfo(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir)
  22. if detectErr != nil {
  23. return fmt.Errorf("read mount info: %v", detectErr)
  24. }
  25. eachEntryFunc, err := makeEventProcessor(remoteStorage, mountedDir, remoteStorageMountLocation, filerSource)
  26. if err != nil {
  27. return err
  28. }
  29. processor := NewMetadataProcessor(eachEntryFunc, 128)
  30. var lastLogTsNs = time.Now().UnixNano()
  31. processEventFnWithOffset := pb.AddOffsetFunc(func(resp *filer_pb.SubscribeMetadataResponse) error {
  32. processor.AddSyncJob(resp)
  33. return nil
  34. }, 3*time.Second, func(counter int64, lastTsNs int64) error {
  35. if processor.processedTsWatermark == 0 {
  36. return nil
  37. }
  38. // use processor.processedTsWatermark instead of the lastTsNs from the most recent job
  39. now := time.Now().UnixNano()
  40. glog.V(0).Infof("remote sync %s progressed to %v %0.2f/sec", *option.filerAddress, time.Unix(0, processor.processedTsWatermark), float64(counter)/(float64(now-lastLogTsNs)/1e9))
  41. lastLogTsNs = now
  42. return remote_storage.SetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir, processor.processedTsWatermark)
  43. })
  44. lastOffsetTs := collectLastSyncOffset(option, option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir, *option.timeAgo)
  45. option.clientEpoch++
  46. return pb.FollowMetadata(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, "filer.remote.sync", option.clientId, option.clientEpoch,
  47. mountedDir, []string{filer.DirectoryEtcRemote}, lastOffsetTs.UnixNano(), 0, 0, processEventFnWithOffset, pb.TrivialOnError)
  48. }
  49. func makeEventProcessor(remoteStorage *remote_pb.RemoteConf, mountedDir string, remoteStorageMountLocation *remote_pb.RemoteStorageLocation, filerSource *source.FilerSource) (pb.ProcessMetadataFunc, error) {
  50. client, err := remote_storage.GetRemoteStorage(remoteStorage)
  51. if err != nil {
  52. return nil, err
  53. }
  54. handleEtcRemoteChanges := func(resp *filer_pb.SubscribeMetadataResponse) error {
  55. message := resp.EventNotification
  56. if message.NewEntry == nil {
  57. return nil
  58. }
  59. if message.NewEntry.Name == filer.REMOTE_STORAGE_MOUNT_FILE {
  60. mappings, readErr := filer.UnmarshalRemoteStorageMappings(message.NewEntry.Content)
  61. if readErr != nil {
  62. return fmt.Errorf("unmarshal mappings: %v", readErr)
  63. }
  64. if remoteLoc, found := mappings.Mappings[mountedDir]; found {
  65. if remoteStorageMountLocation.Bucket != remoteLoc.Bucket || remoteStorageMountLocation.Path != remoteLoc.Path {
  66. glog.Fatalf("Unexpected mount changes %+v => %+v", remoteStorageMountLocation, remoteLoc)
  67. }
  68. } else {
  69. glog.V(0).Infof("unmounted %s exiting ...", mountedDir)
  70. os.Exit(0)
  71. }
  72. }
  73. if message.NewEntry.Name == remoteStorage.Name+filer.REMOTE_STORAGE_CONF_SUFFIX {
  74. conf := &remote_pb.RemoteConf{}
  75. if err := proto.Unmarshal(message.NewEntry.Content, conf); err != nil {
  76. return fmt.Errorf("unmarshal %s/%s: %v", filer.DirectoryEtcRemote, message.NewEntry.Name, err)
  77. }
  78. remoteStorage = conf
  79. if newClient, err := remote_storage.GetRemoteStorage(remoteStorage); err == nil {
  80. client = newClient
  81. } else {
  82. return err
  83. }
  84. }
  85. return nil
  86. }
  87. eachEntryFunc := func(resp *filer_pb.SubscribeMetadataResponse) error {
  88. message := resp.EventNotification
  89. if strings.HasPrefix(resp.Directory, filer.DirectoryEtcRemote) {
  90. return handleEtcRemoteChanges(resp)
  91. }
  92. if filer_pb.IsEmpty(resp) {
  93. return nil
  94. }
  95. if filer_pb.IsCreate(resp) {
  96. if !filer.HasData(message.NewEntry) {
  97. return nil
  98. }
  99. glog.V(2).Infof("create: %+v", resp)
  100. if !shouldSendToRemote(message.NewEntry) {
  101. glog.V(2).Infof("skipping creating: %+v", resp)
  102. return nil
  103. }
  104. dest := toRemoteStorageLocation(util.FullPath(mountedDir), util.NewFullPath(message.NewParentPath, message.NewEntry.Name), remoteStorageMountLocation)
  105. if message.NewEntry.IsDirectory {
  106. glog.V(0).Infof("mkdir %s", remote_storage.FormatLocation(dest))
  107. return client.WriteDirectory(dest, message.NewEntry)
  108. }
  109. glog.V(0).Infof("create %s", remote_storage.FormatLocation(dest))
  110. remoteEntry, writeErr := retriedWriteFile(client, filerSource, message.NewEntry, dest)
  111. if writeErr != nil {
  112. return writeErr
  113. }
  114. return updateLocalEntry(&remoteSyncOptions, message.NewParentPath, message.NewEntry, remoteEntry)
  115. }
  116. if filer_pb.IsDelete(resp) {
  117. glog.V(2).Infof("delete: %+v", resp)
  118. dest := toRemoteStorageLocation(util.FullPath(mountedDir), util.NewFullPath(resp.Directory, message.OldEntry.Name), remoteStorageMountLocation)
  119. if message.OldEntry.IsDirectory {
  120. glog.V(0).Infof("rmdir %s", remote_storage.FormatLocation(dest))
  121. return client.RemoveDirectory(dest)
  122. }
  123. glog.V(0).Infof("delete %s", remote_storage.FormatLocation(dest))
  124. return client.DeleteFile(dest)
  125. }
  126. if message.OldEntry != nil && message.NewEntry != nil {
  127. oldDest := toRemoteStorageLocation(util.FullPath(mountedDir), util.NewFullPath(resp.Directory, message.OldEntry.Name), remoteStorageMountLocation)
  128. dest := toRemoteStorageLocation(util.FullPath(mountedDir), util.NewFullPath(message.NewParentPath, message.NewEntry.Name), remoteStorageMountLocation)
  129. if !shouldSendToRemote(message.NewEntry) {
  130. glog.V(2).Infof("skipping updating: %+v", resp)
  131. return nil
  132. }
  133. if message.NewEntry.IsDirectory {
  134. return client.WriteDirectory(dest, message.NewEntry)
  135. }
  136. if resp.Directory == message.NewParentPath && message.OldEntry.Name == message.NewEntry.Name {
  137. if filer.IsSameData(message.OldEntry, message.NewEntry) {
  138. glog.V(2).Infof("update meta: %+v", resp)
  139. return client.UpdateFileMetadata(dest, message.OldEntry, message.NewEntry)
  140. }
  141. }
  142. glog.V(2).Infof("update: %+v", resp)
  143. glog.V(0).Infof("delete %s", remote_storage.FormatLocation(oldDest))
  144. if err := client.DeleteFile(oldDest); err != nil {
  145. return err
  146. }
  147. remoteEntry, writeErr := retriedWriteFile(client, filerSource, message.NewEntry, dest)
  148. if writeErr != nil {
  149. return writeErr
  150. }
  151. return updateLocalEntry(&remoteSyncOptions, message.NewParentPath, message.NewEntry, remoteEntry)
  152. }
  153. return nil
  154. }
  155. return eachEntryFunc, nil
  156. }
  157. func retriedWriteFile(client remote_storage.RemoteStorageClient, filerSource *source.FilerSource, newEntry *filer_pb.Entry, dest *remote_pb.RemoteStorageLocation) (remoteEntry *filer_pb.RemoteEntry, err error) {
  158. var writeErr error
  159. err = util.Retry("writeFile", func() error {
  160. reader := filer.NewFileReader(filerSource, newEntry)
  161. glog.V(0).Infof("create %s", remote_storage.FormatLocation(dest))
  162. remoteEntry, writeErr = client.WriteFile(dest, newEntry, reader)
  163. if writeErr != nil {
  164. return writeErr
  165. }
  166. return nil
  167. })
  168. if err != nil {
  169. glog.Errorf("write to %s: %v", dest, err)
  170. }
  171. return
  172. }
  173. func collectLastSyncOffset(filerClient filer_pb.FilerClient, grpcDialOption grpc.DialOption, filerAddress pb.ServerAddress, mountedDir string, timeAgo time.Duration) time.Time {
  174. // 1. specified by timeAgo
  175. // 2. last offset timestamp for this directory
  176. // 3. directory creation time
  177. var lastOffsetTs time.Time
  178. if timeAgo == 0 {
  179. mountedDirEntry, err := filer_pb.GetEntry(filerClient, util.FullPath(mountedDir))
  180. if err != nil {
  181. glog.V(0).Infof("get mounted directory %s: %v", mountedDir, err)
  182. return time.Now()
  183. }
  184. lastOffsetTsNs, err := remote_storage.GetSyncOffset(grpcDialOption, filerAddress, mountedDir)
  185. if mountedDirEntry != nil {
  186. if err == nil && mountedDirEntry.Attributes.Crtime < lastOffsetTsNs/1000000 {
  187. lastOffsetTs = time.Unix(0, lastOffsetTsNs)
  188. glog.V(0).Infof("resume from %v", lastOffsetTs)
  189. } else {
  190. lastOffsetTs = time.Unix(mountedDirEntry.Attributes.Crtime, 0)
  191. }
  192. } else {
  193. lastOffsetTs = time.Now()
  194. }
  195. } else {
  196. lastOffsetTs = time.Now().Add(-timeAgo)
  197. }
  198. return lastOffsetTs
  199. }
  200. func toRemoteStorageLocation(mountDir, sourcePath util.FullPath, remoteMountLocation *remote_pb.RemoteStorageLocation) *remote_pb.RemoteStorageLocation {
  201. source := string(sourcePath[len(mountDir):])
  202. dest := util.FullPath(remoteMountLocation.Path).Child(source)
  203. return &remote_pb.RemoteStorageLocation{
  204. Name: remoteMountLocation.Name,
  205. Bucket: remoteMountLocation.Bucket,
  206. Path: string(dest),
  207. }
  208. }
  209. func shouldSendToRemote(entry *filer_pb.Entry) bool {
  210. if entry.RemoteEntry == nil {
  211. return true
  212. }
  213. if entry.RemoteEntry.RemoteMtime < entry.Attributes.Mtime {
  214. return true
  215. }
  216. return false
  217. }
  218. func updateLocalEntry(filerClient filer_pb.FilerClient, dir string, entry *filer_pb.Entry, remoteEntry *filer_pb.RemoteEntry) error {
  219. remoteEntry.LastLocalSyncTsNs = time.Now().UnixNano()
  220. entry.RemoteEntry = remoteEntry
  221. return filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  222. _, err := client.UpdateEntry(context.Background(), &filer_pb.UpdateEntryRequest{
  223. Directory: dir,
  224. Entry: entry,
  225. })
  226. return err
  227. })
  228. }