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.

224 lines
6.9 KiB

7 years ago
7 years ago
7 years ago
6 years ago
7 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
avoid slice out of bounds avoid this problem 2018/09/04 16:27:14 fuse: panic in handler for Write [ID=0x27c0d Node=0x2 Uid=0 Gid=0 Pid=0] 0x1 131072 @10607788032 fl=WriteCache lock=0 ffl=OpenReadOnly: runtime error: slice bounds out of range goroutine 211141 [running]: bazil.org/fuse/fs.(*Server).serve.func2(0x10d3e60, 0xc00014be30, 0xc00052fef8, 0xc00052fe77) /home/travis/gopath/src/bazil.org/fuse/fs/serve.go:857 +0x1ac panic(0xe2d080, 0x17f62b0) /home/travis/.gimme/versions/go/src/runtime/panic.go:513 +0x1b9 github.com/chrislusf/seaweedfs/weed/filesys.(*ContinuousDirtyPages).saveToStorage(0xc0000aca80, 0x10d7ba0, 0xc0003fcc00, 0xc0005dc000, 0x20000, 0x1000000, 0x276720000, 0xc0003feaa0, 0x0, 0x0) /home/travis/gopath/src/github.com/chrislusf/seaweedfs/weed/filesys/dirty_page.go:142 +0x8ec github.com/chrislusf/seaweedfs/weed/filesys.(*ContinuousDirtyPages).saveExistingPagesToStorage(0xc0000aca80, 0x10d7ba0, 0xc0003fcc00, 0x0, 0x0, 0x0) /home/travis/gopath/src/github.com/chrislusf/seaweedfs/weed/filesys/dirty_page.go:107 +0x6c github.com/chrislusf/seaweedfs/weed/filesys.(*ContinuousDirtyPages).AddPage(0xc0000aca80, 0x10d7ba0, 0xc0003fcc00, 0x278460000, 0xc011966050, 0x20000, 0x20fb0, 0x6fc23ac00, 0x4a817c800, 0x0, ...) /home/travis/gopath/src/github.com/chrislusf/seaweedfs/weed/filesys/dirty_page.go:70 +0x8f github.com/chrislusf/seaweedfs/weed/filesys.(*FileHandle).Write(0xc000548410, 0x10d7ba0, 0xc0003fcc00, 0xc00014be30, 0xc011946af8, 0x47fa01, 0x0) /home/travis/gopath/src/github.com/chrislusf/seaweedfs/weed/filesys/filehandle.go:141 +0x245 bazil.org/fuse/fs.(*Server).handleRequest(0xc0002cc0c0, 0x10d7ba0, 0xc0003fcc00, 0x10cb020, 0xc000394140, 0xc0000acac0, 0x10d3e60, 0xc00014be30, 0xc00052fef8, 0x10ca6a0, ...) /home/travis/gopath/src/bazil.org/fuse/fs/serve.go:1265 +0x1599 bazil.org/fuse/fs.(*Server).serve(0xc0002cc0c0, 0x10d3e60, 0xc00014be30) /home/travis/gopath/src/bazil.org/fuse/fs/serve.go:878 +0x410 bazil.org/fuse/fs.(*Server).Serve.func1(0xc0002cc0c0, 0x10d3e60, 0xc00014be30) /home/travis/gopath/src/bazil.org/fuse/fs/serve.go:425 +0x6e created by bazil.org/fuse/fs.(*Server).Serve /home/travis/gopath/src/bazil.org/fuse/fs/serve.go:423 +0x321
6 years ago
  1. package filesys
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "sync"
  7. "sync/atomic"
  8. "time"
  9. "github.com/chrislusf/seaweedfs/weed/glog"
  10. "github.com/chrislusf/seaweedfs/weed/operation"
  11. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  12. "github.com/chrislusf/seaweedfs/weed/security"
  13. )
  14. type ContinuousDirtyPages struct {
  15. hasData bool
  16. Offset int64
  17. Size int64
  18. Data []byte
  19. f *File
  20. lock sync.Mutex
  21. }
  22. func newDirtyPages(file *File) *ContinuousDirtyPages {
  23. return &ContinuousDirtyPages{
  24. Data: nil,
  25. f: file,
  26. }
  27. }
  28. func (pages *ContinuousDirtyPages) releaseResource() {
  29. if pages.Data != nil {
  30. pages.f.wfs.bufPool.Put(pages.Data)
  31. pages.Data = nil
  32. atomic.AddInt32(&counter, -1)
  33. glog.V(3).Infof("%s/%s releasing resource %d", pages.f.dir.Path, pages.f.Name, counter)
  34. }
  35. }
  36. var counter = int32(0)
  37. func (pages *ContinuousDirtyPages) AddPage(ctx context.Context, offset int64, data []byte) (chunks []*filer_pb.FileChunk, err error) {
  38. pages.lock.Lock()
  39. defer pages.lock.Unlock()
  40. var chunk *filer_pb.FileChunk
  41. if len(data) > int(pages.f.wfs.option.ChunkSizeLimit) {
  42. // this is more than what buffer can hold.
  43. return pages.flushAndSave(ctx, offset, data)
  44. }
  45. if pages.Data == nil {
  46. pages.Data = pages.f.wfs.bufPool.Get().([]byte)
  47. atomic.AddInt32(&counter, 1)
  48. glog.V(3).Infof("%s/%s acquire resource %d", pages.f.dir.Path, pages.f.Name, counter)
  49. }
  50. if offset < pages.Offset || offset >= pages.Offset+int64(len(pages.Data)) ||
  51. pages.Offset+int64(len(pages.Data)) < offset+int64(len(data)) {
  52. // if the data is out of range,
  53. // or buffer is full if adding new data,
  54. // flush current buffer and add new data
  55. glog.V(4).Infof("offset=%d, size=%d, existing pages offset=%d, pages size=%d, data=%d", offset, len(data), pages.Offset, pages.Size, len(pages.Data))
  56. if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
  57. if chunk != nil {
  58. glog.V(4).Infof("%s/%s add save [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
  59. chunks = append(chunks, chunk)
  60. }
  61. } else {
  62. glog.V(0).Infof("%s/%s add save [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
  63. return
  64. }
  65. pages.Offset = offset
  66. glog.V(4).Infof("copy data0: offset=%d, size=%d, existing pages offset=%d, pages size=%d, data=%d", offset, len(data), pages.Offset, pages.Size, len(pages.Data))
  67. copy(pages.Data, data)
  68. pages.Size = int64(len(data))
  69. return
  70. }
  71. if offset != pages.Offset+pages.Size {
  72. // when this happens, debug shows the data overlapping with existing data is empty
  73. // the data is not just append
  74. if offset == pages.Offset && int(pages.Size) < len(data) {
  75. glog.V(4).Infof("copy data1: offset=%d, size=%d, existing pages offset=%d, pages size=%d, data=%d", offset, len(data), pages.Offset, pages.Size, len(pages.Data))
  76. copy(pages.Data[pages.Size:], data[pages.Size:])
  77. } else {
  78. if pages.Size != 0 {
  79. glog.V(1).Infof("%s/%s add page: pages [%d, %d) write [%d, %d)", pages.f.dir.Path, pages.f.Name, pages.Offset, pages.Offset+pages.Size, offset, offset+int64(len(data)))
  80. }
  81. return pages.flushAndSave(ctx, offset, data)
  82. }
  83. } else {
  84. glog.V(4).Infof("copy data2: offset=%d, size=%d, existing pages offset=%d, pages size=%d, data=%d", offset, len(data), pages.Offset, pages.Size, len(pages.Data))
  85. copy(pages.Data[offset-pages.Offset:], data)
  86. }
  87. pages.Size = max(pages.Size, offset+int64(len(data))-pages.Offset)
  88. return
  89. }
  90. func (pages *ContinuousDirtyPages) flushAndSave(ctx context.Context, offset int64, data []byte) (chunks []*filer_pb.FileChunk, err error) {
  91. var chunk *filer_pb.FileChunk
  92. // flush existing
  93. if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
  94. if chunk != nil {
  95. glog.V(4).Infof("%s/%s flush existing [%d,%d) to %s", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), chunk.FileId)
  96. chunks = append(chunks, chunk)
  97. }
  98. } else {
  99. glog.V(0).Infof("%s/%s failed to flush1 [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
  100. return
  101. }
  102. pages.Size = 0
  103. pages.Offset = 0
  104. // flush the new page
  105. if chunk, err = pages.saveToStorage(ctx, data, offset); err == nil {
  106. if chunk != nil {
  107. glog.V(4).Infof("%s/%s flush big request [%d,%d) to %s", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), chunk.FileId)
  108. chunks = append(chunks, chunk)
  109. }
  110. } else {
  111. glog.V(0).Infof("%s/%s failed to flush2 [%d,%d): %v", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size), err)
  112. return
  113. }
  114. return
  115. }
  116. func (pages *ContinuousDirtyPages) FlushToStorage(ctx context.Context) (chunk *filer_pb.FileChunk, err error) {
  117. pages.lock.Lock()
  118. defer pages.lock.Unlock()
  119. if pages.Size == 0 {
  120. return nil, nil
  121. }
  122. if chunk, err = pages.saveExistingPagesToStorage(ctx); err == nil {
  123. pages.Size = 0
  124. pages.Offset = 0
  125. if chunk != nil {
  126. glog.V(4).Infof("%s/%s flush [%d,%d)", pages.f.dir.Path, pages.f.Name, chunk.Offset, chunk.Offset+int64(chunk.Size))
  127. }
  128. }
  129. return
  130. }
  131. func (pages *ContinuousDirtyPages) saveExistingPagesToStorage(ctx context.Context) (*filer_pb.FileChunk, error) {
  132. if pages.Size == 0 {
  133. return nil, nil
  134. }
  135. glog.V(0).Infof("%s/%s saveExistingPagesToStorage [%d,%d): Data len=%d", pages.f.dir.Path, pages.f.Name, pages.Offset, pages.Size, len(pages.Data))
  136. return pages.saveToStorage(ctx, pages.Data[:pages.Size], pages.Offset)
  137. }
  138. func (pages *ContinuousDirtyPages) saveToStorage(ctx context.Context, buf []byte, offset int64) (*filer_pb.FileChunk, error) {
  139. var fileId, host string
  140. var auth security.EncodedJwt
  141. if err := pages.f.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  142. request := &filer_pb.AssignVolumeRequest{
  143. Count: 1,
  144. Replication: pages.f.wfs.option.Replication,
  145. Collection: pages.f.wfs.option.Collection,
  146. TtlSec: pages.f.wfs.option.TtlSec,
  147. DataCenter: pages.f.wfs.option.DataCenter,
  148. }
  149. resp, err := client.AssignVolume(ctx, request)
  150. if err != nil {
  151. glog.V(0).Infof("assign volume failure %v: %v", request, err)
  152. return err
  153. }
  154. fileId, host, auth = resp.FileId, resp.Url, security.EncodedJwt(resp.Auth)
  155. return nil
  156. }); err != nil {
  157. return nil, fmt.Errorf("filerGrpcAddress assign volume: %v", err)
  158. }
  159. fileUrl := fmt.Sprintf("http://%s/%s", host, fileId)
  160. bufReader := bytes.NewReader(buf)
  161. uploadResult, err := operation.Upload(fileUrl, pages.f.Name, bufReader, false, "", nil, auth)
  162. if err != nil {
  163. glog.V(0).Infof("upload data %v to %s: %v", pages.f.Name, fileUrl, err)
  164. return nil, fmt.Errorf("upload data: %v", err)
  165. }
  166. if uploadResult.Error != "" {
  167. glog.V(0).Infof("upload failure %v to %s: %v", pages.f.Name, fileUrl, err)
  168. return nil, fmt.Errorf("upload result: %v", uploadResult.Error)
  169. }
  170. return &filer_pb.FileChunk{
  171. FileId: fileId,
  172. Offset: offset,
  173. Size: uint64(len(buf)),
  174. Mtime: time.Now().UnixNano(),
  175. ETag: uploadResult.ETag,
  176. }, nil
  177. }
  178. func max(x, y int64) int64 {
  179. if x > y {
  180. return x
  181. }
  182. return y
  183. }