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.

184 lines
4.3 KiB

  1. package filer
  2. import (
  3. "fmt"
  4. "github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
  5. "github.com/chrislusf/seaweedfs/weed/util/mem"
  6. "github.com/chrislusf/seaweedfs/weed/wdclient"
  7. "sync"
  8. "time"
  9. )
  10. type ReaderCache struct {
  11. chunkCache chunk_cache.ChunkCache
  12. lookupFileIdFn wdclient.LookupFileIdFunctionType
  13. sync.Mutex
  14. downloaders map[string]*SingleChunkCacher
  15. limit int
  16. }
  17. type SingleChunkCacher struct {
  18. sync.RWMutex
  19. parent *ReaderCache
  20. chunkFileId string
  21. data []byte
  22. err error
  23. cipherKey []byte
  24. isGzipped bool
  25. chunkSize int
  26. shouldCache bool
  27. wg sync.WaitGroup
  28. completedTime time.Time
  29. }
  30. func newReaderCache(limit int, chunkCache chunk_cache.ChunkCache, lookupFileIdFn wdclient.LookupFileIdFunctionType) *ReaderCache {
  31. return &ReaderCache{
  32. limit: limit,
  33. chunkCache: chunkCache,
  34. lookupFileIdFn: lookupFileIdFn,
  35. downloaders: make(map[string]*SingleChunkCacher),
  36. }
  37. }
  38. func (rc *ReaderCache) MaybeCache(fileId string, cipherKey []byte, isGzipped bool, chunkSize int) {
  39. rc.Lock()
  40. defer rc.Unlock()
  41. if _, found := rc.downloaders[fileId]; found {
  42. return
  43. }
  44. if rc.lookupFileIdFn == nil {
  45. return
  46. }
  47. // if too many, delete one of them?
  48. if len(rc.downloaders) >= rc.limit {
  49. oldestFid, oldestTime := "", time.Now()
  50. for fid, downloader := range rc.downloaders {
  51. if !downloader.completedTime.IsZero() {
  52. if downloader.completedTime.Before(oldestTime) {
  53. oldestFid, oldestTime = fid, downloader.completedTime
  54. }
  55. }
  56. }
  57. if oldestFid != "" {
  58. oldDownloader := rc.downloaders[oldestFid]
  59. delete(rc.downloaders, oldestFid)
  60. oldDownloader.destroy()
  61. } else {
  62. // if still no slots, return
  63. return
  64. }
  65. }
  66. cacher := newSingleChunkCacher(rc, fileId, cipherKey, isGzipped, chunkSize, false)
  67. cacher.wg.Add(1)
  68. go cacher.startCaching()
  69. cacher.wg.Wait()
  70. rc.downloaders[fileId] = cacher
  71. return
  72. }
  73. func (rc *ReaderCache) ReadChunkAt(buffer []byte, fileId string, cipherKey []byte, isGzipped bool, offset int64, chunkSize int, shouldCache bool) (int, error) {
  74. rc.Lock()
  75. defer rc.Unlock()
  76. if cacher, found := rc.downloaders[fileId]; found {
  77. return cacher.readChunkAt(buffer, offset)
  78. }
  79. if shouldCache || rc.lookupFileIdFn == nil {
  80. n, err := rc.chunkCache.ReadChunkAt(buffer, fileId, uint64(offset))
  81. if n > 0 {
  82. return n, err
  83. }
  84. }
  85. if len(rc.downloaders) >= rc.limit {
  86. oldestFid, oldestTime := "", time.Now()
  87. for fid, downloader := range rc.downloaders {
  88. if !downloader.completedTime.IsZero() {
  89. if downloader.completedTime.Before(oldestTime) {
  90. oldestFid, oldestTime = fid, downloader.completedTime
  91. }
  92. }
  93. }
  94. if oldestFid != "" {
  95. oldDownloader := rc.downloaders[oldestFid]
  96. delete(rc.downloaders, oldestFid)
  97. oldDownloader.destroy()
  98. }
  99. }
  100. cacher := newSingleChunkCacher(rc, fileId, cipherKey, isGzipped, chunkSize, shouldCache)
  101. cacher.wg.Add(1)
  102. go cacher.startCaching()
  103. cacher.wg.Wait()
  104. rc.downloaders[fileId] = cacher
  105. return cacher.readChunkAt(buffer, offset)
  106. }
  107. func (rc *ReaderCache) destroy() {
  108. rc.Lock()
  109. defer rc.Unlock()
  110. for _, downloader := range rc.downloaders {
  111. downloader.destroy()
  112. }
  113. }
  114. func newSingleChunkCacher(parent *ReaderCache, fileId string, cipherKey []byte, isGzipped bool, chunkSize int, shouldCache bool) *SingleChunkCacher {
  115. t := &SingleChunkCacher{
  116. parent: parent,
  117. chunkFileId: fileId,
  118. cipherKey: cipherKey,
  119. isGzipped: isGzipped,
  120. chunkSize: chunkSize,
  121. shouldCache: shouldCache,
  122. }
  123. return t
  124. }
  125. func (s *SingleChunkCacher) startCaching() {
  126. s.Lock()
  127. defer s.Unlock()
  128. s.wg.Done() // means this has been started
  129. urlStrings, err := s.parent.lookupFileIdFn(s.chunkFileId)
  130. if err != nil {
  131. s.err = fmt.Errorf("operation LookupFileId %s failed, err: %v", s.chunkFileId, err)
  132. return
  133. }
  134. s.data = mem.Allocate(s.chunkSize)
  135. _, s.err = retriedFetchChunkData(s.data, urlStrings, s.cipherKey, s.isGzipped, true, 0)
  136. if s.err != nil {
  137. mem.Free(s.data)
  138. s.data = nil
  139. return
  140. }
  141. s.completedTime = time.Now()
  142. if s.shouldCache {
  143. s.parent.chunkCache.SetChunk(s.chunkFileId, s.data)
  144. }
  145. return
  146. }
  147. func (s *SingleChunkCacher) destroy() {
  148. if s.data != nil {
  149. mem.Free(s.data)
  150. s.data = nil
  151. }
  152. }
  153. func (s *SingleChunkCacher) readChunkAt(buf []byte, offset int64) (int, error) {
  154. s.RLock()
  155. defer s.RUnlock()
  156. return copy(buf, s.data[offset:]), s.err
  157. }