filer_grpc_server_sub_meta.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. package weed_server
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "sync/atomic"
  7. "time"
  8. "github.com/seaweedfs/seaweedfs/weed/stats"
  9. "google.golang.org/protobuf/proto"
  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/util"
  14. "github.com/seaweedfs/seaweedfs/weed/util/log_buffer"
  15. )
  16. const (
  17. // MaxUnsyncedEvents send empty notification with timestamp when certain amount of events have been filtered
  18. MaxUnsyncedEvents = 1e3
  19. )
  20. func (fs *FilerServer) SubscribeMetadata(req *filer_pb.SubscribeMetadataRequest, stream filer_pb.SeaweedFiler_SubscribeMetadataServer) error {
  21. ctx := stream.Context()
  22. peerAddress := findClientAddress(ctx, 0)
  23. isReplacing, alreadyKnown, clientName := fs.addClient("", req.ClientName, peerAddress, req.ClientId, req.ClientEpoch)
  24. if isReplacing {
  25. fs.filer.MetaAggregator.ListenersCond.Broadcast() // nudges the subscribers that are waiting
  26. } else if alreadyKnown {
  27. fs.filer.MetaAggregator.ListenersCond.Broadcast() // nudges the subscribers that are waiting
  28. return fmt.Errorf("duplicated subscription detected for client %s id %d", clientName, req.ClientId)
  29. }
  30. defer func() {
  31. glog.V(0).Infof("disconnect %v subscriber %s clientId:%d", clientName, req.PathPrefix, req.ClientId)
  32. fs.deleteClient("", clientName, req.ClientId, req.ClientEpoch)
  33. fs.filer.MetaAggregator.ListenersCond.Broadcast() // nudges the subscribers that are waiting
  34. }()
  35. lastReadTime := log_buffer.NewMessagePosition(req.SinceNs, -2)
  36. glog.V(0).Infof(" %v starts to subscribe %s from %+v", clientName, req.PathPrefix, lastReadTime)
  37. eachEventNotificationFn := fs.eachEventNotificationFn(req, stream, clientName)
  38. eachLogEntryFn := eachLogEntryFn(eachEventNotificationFn)
  39. var processedTsNs int64
  40. var readPersistedLogErr error
  41. var readInMemoryLogErr error
  42. var isDone bool
  43. for {
  44. glog.V(4).Infof("read on disk %v aggregated subscribe %s from %+v", clientName, req.PathPrefix, lastReadTime)
  45. processedTsNs, isDone, readPersistedLogErr = fs.filer.ReadPersistedLogBuffer(lastReadTime, req.UntilNs, eachLogEntryFn)
  46. if readPersistedLogErr != nil {
  47. return fmt.Errorf("reading from persisted logs: %w", readPersistedLogErr)
  48. }
  49. if isDone {
  50. return nil
  51. }
  52. glog.V(4).Infof("processed to %v: %v", clientName, processedTsNs)
  53. if processedTsNs != 0 {
  54. lastReadTime = log_buffer.NewMessagePosition(processedTsNs, -2)
  55. } else {
  56. nextDayTs := util.GetNextDayTsNano(lastReadTime.UnixNano())
  57. position := log_buffer.NewMessagePosition(nextDayTs, -2)
  58. found, err := fs.filer.HasPersistedLogFiles(position)
  59. if err != nil {
  60. return fmt.Errorf("checking persisted log files: %w", err)
  61. }
  62. if found {
  63. lastReadTime = position
  64. }
  65. }
  66. glog.V(4).Infof("read in memory %v aggregated subscribe %s from %+v", clientName, req.PathPrefix, lastReadTime)
  67. lastReadTime, isDone, readInMemoryLogErr = fs.filer.MetaAggregator.MetaLogBuffer.LoopProcessLogData("aggMeta:"+clientName, lastReadTime, req.UntilNs, func() bool {
  68. // Check if the client has disconnected by monitoring the context
  69. select {
  70. case <-ctx.Done():
  71. return false
  72. default:
  73. }
  74. fs.filer.MetaAggregator.ListenersLock.Lock()
  75. fs.filer.MetaAggregator.ListenersCond.Wait()
  76. fs.filer.MetaAggregator.ListenersLock.Unlock()
  77. return fs.hasClient(req.ClientId, req.ClientEpoch)
  78. }, eachLogEntryFn)
  79. if readInMemoryLogErr != nil {
  80. if errors.Is(readInMemoryLogErr, log_buffer.ResumeFromDiskError) {
  81. continue
  82. }
  83. glog.Errorf("processed to %v: %v", lastReadTime, readInMemoryLogErr)
  84. if !errors.Is(readInMemoryLogErr, log_buffer.ResumeError) {
  85. break
  86. }
  87. }
  88. if isDone {
  89. return nil
  90. }
  91. if !fs.hasClient(req.ClientId, req.ClientEpoch) {
  92. glog.V(0).Infof("client %v is closed", clientName)
  93. return nil
  94. }
  95. time.Sleep(1127 * time.Millisecond)
  96. }
  97. return readInMemoryLogErr
  98. }
  99. func (fs *FilerServer) SubscribeLocalMetadata(req *filer_pb.SubscribeMetadataRequest, stream filer_pb.SeaweedFiler_SubscribeLocalMetadataServer) error {
  100. ctx := stream.Context()
  101. peerAddress := findClientAddress(ctx, 0)
  102. // use negative client id to differentiate from addClient()/deleteClient() used in SubscribeMetadata()
  103. req.ClientId = -req.ClientId
  104. isReplacing, alreadyKnown, clientName := fs.addClient("local", req.ClientName, peerAddress, req.ClientId, req.ClientEpoch)
  105. if isReplacing {
  106. fs.listenersCond.Broadcast() // nudges the subscribers that are waiting
  107. } else if alreadyKnown {
  108. return fmt.Errorf("duplicated local subscription detected for client %s clientId:%d", clientName, req.ClientId)
  109. }
  110. defer func() {
  111. glog.V(0).Infof("disconnect %v local subscriber %s clientId:%d", clientName, req.PathPrefix, req.ClientId)
  112. fs.deleteClient("local", clientName, req.ClientId, req.ClientEpoch)
  113. fs.listenersCond.Broadcast() // nudges the subscribers that are waiting
  114. }()
  115. lastReadTime := log_buffer.NewMessagePosition(req.SinceNs, -2)
  116. glog.V(0).Infof(" + %v local subscribe %s from %+v clientId:%d", clientName, req.PathPrefix, lastReadTime, req.ClientId)
  117. eachEventNotificationFn := fs.eachEventNotificationFn(req, stream, clientName)
  118. eachLogEntryFn := eachLogEntryFn(eachEventNotificationFn)
  119. var processedTsNs int64
  120. var readPersistedLogErr error
  121. var readInMemoryLogErr error
  122. var isDone bool
  123. for {
  124. // println("reading from persisted logs ...")
  125. glog.V(0).Infof("read on disk %v local subscribe %s from %+v", clientName, req.PathPrefix, lastReadTime)
  126. processedTsNs, isDone, readPersistedLogErr = fs.filer.ReadPersistedLogBuffer(lastReadTime, req.UntilNs, eachLogEntryFn)
  127. if readPersistedLogErr != nil {
  128. glog.V(0).Infof("read on disk %v local subscribe %s from %+v: %v", clientName, req.PathPrefix, lastReadTime, readPersistedLogErr)
  129. return fmt.Errorf("reading from persisted logs: %w", readPersistedLogErr)
  130. }
  131. if isDone {
  132. return nil
  133. }
  134. if processedTsNs != 0 {
  135. lastReadTime = log_buffer.NewMessagePosition(processedTsNs, -2)
  136. } else {
  137. if readInMemoryLogErr == log_buffer.ResumeFromDiskError {
  138. time.Sleep(1127 * time.Millisecond)
  139. continue
  140. }
  141. // If no persisted entries were read for this day, check the next day for logs
  142. nextDayTs := util.GetNextDayTsNano(lastReadTime.UnixNano())
  143. position := log_buffer.NewMessagePosition(nextDayTs, -2)
  144. found, err := fs.filer.HasPersistedLogFiles(position)
  145. if err != nil {
  146. return fmt.Errorf("checking persisted log files: %w", err)
  147. }
  148. if found {
  149. lastReadTime = position
  150. }
  151. }
  152. glog.V(0).Infof("read in memory %v local subscribe %s from %+v", clientName, req.PathPrefix, lastReadTime)
  153. lastReadTime, isDone, readInMemoryLogErr = fs.filer.LocalMetaLogBuffer.LoopProcessLogData("localMeta:"+clientName, lastReadTime, req.UntilNs, func() bool {
  154. // Check if the client has disconnected by monitoring the context
  155. select {
  156. case <-ctx.Done():
  157. return false
  158. default:
  159. }
  160. fs.listenersLock.Lock()
  161. atomic.AddInt64(&fs.listenersWaits, 1)
  162. fs.listenersCond.Wait()
  163. atomic.AddInt64(&fs.listenersWaits, -1)
  164. fs.listenersLock.Unlock()
  165. if !fs.hasClient(req.ClientId, req.ClientEpoch) {
  166. return false
  167. }
  168. return true
  169. }, eachLogEntryFn)
  170. if readInMemoryLogErr != nil {
  171. if readInMemoryLogErr == log_buffer.ResumeFromDiskError {
  172. continue
  173. }
  174. glog.Errorf("processed to %v: %v", lastReadTime, readInMemoryLogErr)
  175. if readInMemoryLogErr != log_buffer.ResumeError {
  176. break
  177. }
  178. }
  179. if isDone {
  180. return nil
  181. }
  182. if !fs.hasClient(req.ClientId, req.ClientEpoch) {
  183. return nil
  184. }
  185. }
  186. return readInMemoryLogErr
  187. }
  188. func eachLogEntryFn(eachEventNotificationFn func(dirPath string, eventNotification *filer_pb.EventNotification, tsNs int64) error) log_buffer.EachLogEntryFuncType {
  189. return func(logEntry *filer_pb.LogEntry) (bool, error) {
  190. event := &filer_pb.SubscribeMetadataResponse{}
  191. if err := proto.Unmarshal(logEntry.Data, event); err != nil {
  192. glog.Errorf("unexpected unmarshal filer_pb.SubscribeMetadataResponse: %v", err)
  193. return false, fmt.Errorf("unexpected unmarshal filer_pb.SubscribeMetadataResponse: %w", err)
  194. }
  195. if err := eachEventNotificationFn(event.Directory, event.EventNotification, event.TsNs); err != nil {
  196. return false, err
  197. }
  198. return false, nil
  199. }
  200. }
  201. func (fs *FilerServer) eachEventNotificationFn(req *filer_pb.SubscribeMetadataRequest, stream filer_pb.SeaweedFiler_SubscribeMetadataServer, clientName string) func(dirPath string, eventNotification *filer_pb.EventNotification, tsNs int64) error {
  202. filtered := 0
  203. return func(dirPath string, eventNotification *filer_pb.EventNotification, tsNs int64) error {
  204. defer func() {
  205. if filtered > MaxUnsyncedEvents {
  206. if err := stream.Send(&filer_pb.SubscribeMetadataResponse{
  207. EventNotification: &filer_pb.EventNotification{},
  208. TsNs: tsNs,
  209. }); err == nil {
  210. filtered = 0
  211. }
  212. }
  213. }()
  214. filtered++
  215. foundSelf := false
  216. for _, sig := range eventNotification.Signatures {
  217. if sig == req.Signature && req.Signature != 0 {
  218. return nil
  219. }
  220. if sig == fs.filer.Signature {
  221. foundSelf = true
  222. }
  223. }
  224. if !foundSelf {
  225. eventNotification.Signatures = append(eventNotification.Signatures, fs.filer.Signature)
  226. }
  227. // get complete path to the file or directory
  228. var entryName string
  229. if eventNotification.OldEntry != nil {
  230. entryName = eventNotification.OldEntry.Name
  231. } else if eventNotification.NewEntry != nil {
  232. entryName = eventNotification.NewEntry.Name
  233. }
  234. fullpath := util.Join(dirPath, entryName)
  235. // skip on filer internal meta logs
  236. if strings.HasPrefix(fullpath, filer.SystemLogDir) {
  237. return nil
  238. }
  239. if hasPrefixIn(fullpath, req.PathPrefixes) {
  240. // good
  241. } else if matchByDirectory(dirPath, req.Directories) {
  242. // good
  243. } else {
  244. if !strings.HasPrefix(fullpath, req.PathPrefix) {
  245. if eventNotification.NewParentPath != "" {
  246. newFullPath := util.Join(eventNotification.NewParentPath, entryName)
  247. if !strings.HasPrefix(newFullPath, req.PathPrefix) {
  248. return nil
  249. }
  250. } else {
  251. return nil
  252. }
  253. }
  254. }
  255. // collect timestamps for path
  256. stats.FilerServerLastSendTsOfSubscribeGauge.WithLabelValues(fs.option.Host.String(), req.ClientName, req.PathPrefix).Set(float64(tsNs))
  257. message := &filer_pb.SubscribeMetadataResponse{
  258. Directory: dirPath,
  259. EventNotification: eventNotification,
  260. TsNs: tsNs,
  261. }
  262. // println("sending", dirPath, entryName)
  263. if err := stream.Send(message); err != nil {
  264. glog.V(0).Infof("=> client %v: %+v", clientName, err)
  265. return err
  266. }
  267. filtered = 0
  268. return nil
  269. }
  270. }
  271. func hasPrefixIn(text string, prefixes []string) bool {
  272. for _, p := range prefixes {
  273. if strings.HasPrefix(text, p) {
  274. return true
  275. }
  276. }
  277. return false
  278. }
  279. func matchByDirectory(dirPath string, directories []string) bool {
  280. for _, dir := range directories {
  281. if dirPath == dir {
  282. return true
  283. }
  284. }
  285. return false
  286. }
  287. func (fs *FilerServer) addClient(prefix string, clientType string, clientAddress string, clientId int32, clientEpoch int32) (isReplacing, alreadyKnown bool, clientName string) {
  288. clientName = clientType + "@" + clientAddress
  289. glog.V(0).Infof("+ %v listener %v clientId %v clientEpoch %v", prefix, clientName, clientId, clientEpoch)
  290. if clientId != 0 {
  291. fs.knownListenersLock.Lock()
  292. defer fs.knownListenersLock.Unlock()
  293. epoch, found := fs.knownListeners[clientId]
  294. if !found || epoch < clientEpoch {
  295. fs.knownListeners[clientId] = clientEpoch
  296. isReplacing = true
  297. } else {
  298. alreadyKnown = true
  299. }
  300. }
  301. return
  302. }
  303. func (fs *FilerServer) deleteClient(prefix string, clientName string, clientId int32, clientEpoch int32) {
  304. glog.V(0).Infof("- %v listener %v clientId %v clientEpoch %v", prefix, clientName, clientId, clientEpoch)
  305. if clientId != 0 {
  306. fs.knownListenersLock.Lock()
  307. defer fs.knownListenersLock.Unlock()
  308. epoch, found := fs.knownListeners[clientId]
  309. if found && epoch <= clientEpoch {
  310. delete(fs.knownListeners, clientId)
  311. }
  312. }
  313. }
  314. func (fs *FilerServer) hasClient(clientId int32, clientEpoch int32) bool {
  315. if clientId != 0 {
  316. fs.knownListenersLock.Lock()
  317. defer fs.knownListenersLock.Unlock()
  318. epoch, found := fs.knownListeners[clientId]
  319. if found && epoch <= clientEpoch {
  320. return true
  321. }
  322. }
  323. return false
  324. }