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.
		
		
		
		
		
			
		
			
				
					
					
						
							202 lines
						
					
					
						
							5.3 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							202 lines
						
					
					
						
							5.3 KiB
						
					
					
				
								package page_writer
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util/mem"
							 | 
						|
									"io"
							 | 
						|
									"os"
							 | 
						|
									"sync"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								var (
							 | 
						|
									_ = PageChunk(&SwapFileChunk{})
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type ActualChunkIndex int
							 | 
						|
								
							 | 
						|
								type SwapFile struct {
							 | 
						|
									dir                 string
							 | 
						|
									file                *os.File
							 | 
						|
									chunkSize           int64
							 | 
						|
									chunkTrackingLock   sync.Mutex
							 | 
						|
									activeChunkCount    int
							 | 
						|
									freeActualChunkList []ActualChunkIndex
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type SwapFileChunk struct {
							 | 
						|
									sync.RWMutex
							 | 
						|
									swapfile         *SwapFile
							 | 
						|
									usage            *ChunkWrittenIntervalList
							 | 
						|
									logicChunkIndex  LogicChunkIndex
							 | 
						|
									actualChunkIndex ActualChunkIndex
							 | 
						|
									activityScore    *ActivityScore
							 | 
						|
									//memChunk         *MemChunk
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func NewSwapFile(dir string, chunkSize int64) *SwapFile {
							 | 
						|
									return &SwapFile{
							 | 
						|
										dir:       dir,
							 | 
						|
										file:      nil,
							 | 
						|
										chunkSize: chunkSize,
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								func (sf *SwapFile) FreeResource() {
							 | 
						|
									if sf.file != nil {
							 | 
						|
										sf.file.Close()
							 | 
						|
										os.Remove(sf.file.Name())
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sf *SwapFile) NewSwapFileChunk(logicChunkIndex LogicChunkIndex) (tc *SwapFileChunk) {
							 | 
						|
									if sf.file == nil {
							 | 
						|
										var err error
							 | 
						|
										sf.file, err = os.CreateTemp(sf.dir, "")
							 | 
						|
										if err != nil {
							 | 
						|
											glog.Errorf("create swap file: %v", err)
							 | 
						|
											return nil
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									sf.chunkTrackingLock.Lock()
							 | 
						|
									defer sf.chunkTrackingLock.Unlock()
							 | 
						|
								
							 | 
						|
									sf.activeChunkCount++
							 | 
						|
								
							 | 
						|
									// assign a new physical chunk
							 | 
						|
									var actualChunkIndex ActualChunkIndex
							 | 
						|
									if len(sf.freeActualChunkList) > 0 {
							 | 
						|
										actualChunkIndex = sf.freeActualChunkList[0]
							 | 
						|
										sf.freeActualChunkList = sf.freeActualChunkList[1:]
							 | 
						|
									} else {
							 | 
						|
										actualChunkIndex = ActualChunkIndex(sf.activeChunkCount)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									swapFileChunk := &SwapFileChunk{
							 | 
						|
										swapfile:         sf,
							 | 
						|
										usage:            newChunkWrittenIntervalList(),
							 | 
						|
										logicChunkIndex:  logicChunkIndex,
							 | 
						|
										actualChunkIndex: actualChunkIndex,
							 | 
						|
										activityScore:    NewActivityScore(),
							 | 
						|
										// memChunk:         NewMemChunk(logicChunkIndex, sf.chunkSize),
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// println(logicChunkIndex, "|", "++++", swapFileChunk.actualChunkIndex, swapFileChunk, sf)
							 | 
						|
									return swapFileChunk
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) FreeResource() {
							 | 
						|
								
							 | 
						|
									sc.Lock()
							 | 
						|
									defer sc.Unlock()
							 | 
						|
								
							 | 
						|
									sc.swapfile.chunkTrackingLock.Lock()
							 | 
						|
									defer sc.swapfile.chunkTrackingLock.Unlock()
							 | 
						|
								
							 | 
						|
									sc.swapfile.freeActualChunkList = append(sc.swapfile.freeActualChunkList, sc.actualChunkIndex)
							 | 
						|
									sc.swapfile.activeChunkCount--
							 | 
						|
									// println(sc.logicChunkIndex, "|", "----", sc.actualChunkIndex, sc, sc.swapfile)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) WriteDataAt(src []byte, offset int64, tsNs int64) (n int) {
							 | 
						|
									sc.Lock()
							 | 
						|
									defer sc.Unlock()
							 | 
						|
								
							 | 
						|
									// println(sc.logicChunkIndex, "|", tsNs, "write at", offset, len(src), sc.actualChunkIndex)
							 | 
						|
								
							 | 
						|
									innerOffset := offset % sc.swapfile.chunkSize
							 | 
						|
									var err error
							 | 
						|
									n, err = sc.swapfile.file.WriteAt(src, int64(sc.actualChunkIndex)*sc.swapfile.chunkSize+innerOffset)
							 | 
						|
									sc.usage.MarkWritten(innerOffset, innerOffset+int64(n), tsNs)
							 | 
						|
									if err != nil {
							 | 
						|
										glog.Errorf("failed to write swap file %s: %v", sc.swapfile.file.Name(), err)
							 | 
						|
									}
							 | 
						|
									//sc.memChunk.WriteDataAt(src, offset, tsNs)
							 | 
						|
									sc.activityScore.MarkWrite()
							 | 
						|
								
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) ReadDataAt(p []byte, off int64, tsNs int64) (maxStop int64) {
							 | 
						|
									sc.RLock()
							 | 
						|
									defer sc.RUnlock()
							 | 
						|
								
							 | 
						|
									// println(sc.logicChunkIndex, "|", tsNs, "read at", off, len(p), sc.actualChunkIndex)
							 | 
						|
								
							 | 
						|
									//memCopy := make([]byte, len(p))
							 | 
						|
									//copy(memCopy, p)
							 | 
						|
								
							 | 
						|
									chunkStartOffset := int64(sc.logicChunkIndex) * sc.swapfile.chunkSize
							 | 
						|
									for t := sc.usage.head.next; t != sc.usage.tail; t = t.next {
							 | 
						|
										logicStart := max(off, chunkStartOffset+t.StartOffset)
							 | 
						|
										logicStop := min(off+int64(len(p)), chunkStartOffset+t.stopOffset)
							 | 
						|
										if logicStart < logicStop {
							 | 
						|
											actualStart := logicStart - chunkStartOffset + int64(sc.actualChunkIndex)*sc.swapfile.chunkSize
							 | 
						|
											if n, err := sc.swapfile.file.ReadAt(p[logicStart-off:logicStop-off], actualStart); err != nil {
							 | 
						|
												if err == io.EOF && n == int(logicStop-logicStart) {
							 | 
						|
													err = nil
							 | 
						|
												}
							 | 
						|
												glog.Errorf("failed to reading swap file %s: %v", sc.swapfile.file.Name(), err)
							 | 
						|
												break
							 | 
						|
											}
							 | 
						|
											maxStop = max(maxStop, logicStop)
							 | 
						|
								
							 | 
						|
											if t.TsNs > tsNs {
							 | 
						|
												println("read new data2", t.TsNs-tsNs, "ns")
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									//sc.memChunk.ReadDataAt(memCopy, off, tsNs)
							 | 
						|
									//if bytes.Compare(memCopy, p) != 0 {
							 | 
						|
									//	println("read wrong data from swap file", off, sc.logicChunkIndex)
							 | 
						|
									//}
							 | 
						|
								
							 | 
						|
									sc.activityScore.MarkRead()
							 | 
						|
								
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) IsComplete() bool {
							 | 
						|
									sc.RLock()
							 | 
						|
									defer sc.RUnlock()
							 | 
						|
									return sc.usage.IsComplete(sc.swapfile.chunkSize)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) ActivityScore() int64 {
							 | 
						|
									return sc.activityScore.ActivityScore()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) WrittenSize() int64 {
							 | 
						|
									sc.RLock()
							 | 
						|
									defer sc.RUnlock()
							 | 
						|
									return sc.usage.WrittenSize()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (sc *SwapFileChunk) SaveContent(saveFn SaveToStorageFunc) {
							 | 
						|
									sc.RLock()
							 | 
						|
									defer sc.RUnlock()
							 | 
						|
								
							 | 
						|
									if saveFn == nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									// println(sc.logicChunkIndex, "|", "save")
							 | 
						|
									for t := sc.usage.head.next; t != sc.usage.tail; t = t.next {
							 | 
						|
										startOffset := t.StartOffset
							 | 
						|
										stopOffset := t.stopOffset
							 | 
						|
										tsNs := t.TsNs
							 | 
						|
										for t != sc.usage.tail && t.next.StartOffset == stopOffset {
							 | 
						|
											stopOffset = t.next.stopOffset
							 | 
						|
											t = t.next
							 | 
						|
											tsNs = max(tsNs, t.TsNs)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										data := mem.Allocate(int(stopOffset - startOffset))
							 | 
						|
										n, _ := sc.swapfile.file.ReadAt(data, startOffset+int64(sc.actualChunkIndex)*sc.swapfile.chunkSize)
							 | 
						|
										if n > 0 {
							 | 
						|
											reader := util.NewBytesReader(data[:n])
							 | 
						|
											saveFn(reader, int64(sc.logicChunkIndex)*sc.swapfile.chunkSize+startOffset, int64(n), tsNs, func() {
							 | 
						|
											})
							 | 
						|
										}
							 | 
						|
										mem.Free(data)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 |