func NewCollection(file string, cacheSize int) (*Collection, error) {
	f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		return nil, err
	}
	err = f.Truncate(0)
	if err != nil {
		f.Close()
		return nil, err
	}
	wal, err := newWAL(file + ".wal")
	if err != nil {
		f.Close()
		return nil, err
	}
	c := &Collection{
		f:       f,
		wal:     wal,
		cache:   newCache(cacheSize),
		readAt:  f.ReadAt,
		writeAt: f.WriteAt,
	}
	c.fileHeader.Version = fileVersion
	c.fileHeader.Next[0] = 0
	c.fileHeader.LastCommit = int64(512)
	c.f.Seek(0, 0)
	err = binary.Write(c.f, binary.LittleEndian, c.fileHeader)
	if err != nil {
		c.f.Close()
		c.wal.Close()
		return nil, err
	}
	err = c.f.Truncate(c.LastCommit)
	if err != nil {
		c.f.Close()
		c.wal.Close()
		return nil, err
	}
	return c, nil
}
NewCollection creates a new collection with a data file at file.
cacheSize represents the size of the collection cache.
package lm2
import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"sync"
	"sync/atomic"
)
const sentinelMagic = 0xDEAD10CC
const (
	maxLevels = 4
	levelProb = 0.1
	cacheProb = 0.1
)
var (
	ErrDoesNotExist = errors.New("lm2: does not exist")
	ErrInternal = errors.New("lm2: internal error")
	ErrKeyNotFound = errors.New("lm2: key not found")
	fileVersion = [8]byte{'l', 'm', '2', '_', '0', '0', '1', '\n'}
)
type RollbackError struct {
	DuplicateKey  bool
	ConflictedKey string
	Err           error
}
func (e RollbackError) Error() string {
	if e.DuplicateKey {
		return fmt.Sprintf("lm2: rolled back due to duplicate key (conflicted key: `%s`)",
			e.ConflictedKey)
	}
	return fmt.Sprintf("lm2: rolled back (%s)", e.Err.Error())
}
func IsRollbackError(err error) bool {
	_, ok := err.(RollbackError)
	return ok
}
type Collection struct {
	fileHeader
	f         *os.File
	wal       *wal
	stats     Stats
	dirty     map[int64]*record
	cache     *recordCache
	dirtyLock sync.Mutex
	internalState uint32
	metaLock  sync.RWMutex
	writeLock sync.Mutex
	readAt  func(b []byte, off int64) (n int, err error)
	writeAt func(b []byte, off int64) (n int, err error)
}
type fileHeader struct {
	Version    [8]byte
	Next       [maxLevels]int64
	LastCommit int64
}
func (h fileHeader) bytes() []byte {
	buf := bytes.NewBuffer(nil)
	binary.Write(buf, binary.LittleEndian, h)
	return buf.Bytes()
}
type recordHeader struct {
	_       uint8  
	_       uint8  
	Next    [maxLevels]int64
	Deleted int64
	KeyLen  uint16
	ValLen  uint32
}
const recordHeaderSize = 2 + (maxLevels * 8) + 8 + 2 + 4
func (h recordHeader) bytes() []byte {
	buf := bytes.NewBuffer(nil)
	binary.Write(buf, binary.LittleEndian, h)
	return buf.Bytes()
}
type sentinelRecord struct {
	Magic  uint32  
	Offset int64   
}
type record struct {
	recordHeader
	Offset int64
	Key    string
	Value  string
}
func generateLevel() int {
	level := 0
	for i := 0; i < maxLevels-1; i++ {
		if rand.Float32() <= levelProb {
			level++
		} else {
			break
		}
	}
	return level
}
func (c *Collection) getDirty(offset int64) *record {
	c.dirtyLock.Lock()
	defer c.dirtyLock.Unlock()
	if c.dirty == nil {
		return nil
	}
	return c.dirty[offset]
}
func (c *Collection) setDirty(offset int64, rec *record) {
	c.dirtyLock.Lock()
	defer c.dirtyLock.Unlock()
	c.dirty[offset] = rec
}
func (c *Collection) readRecord(offset int64, dirty bool) (*record, error) {
	if offset == 0 {
		return nil, errors.New("lm2: invalid record offset 0")
	}
	if dirty {
		if rec := c.getDirty(offset); rec != nil {
			return rec, nil
		}
	}
	c.cache.lock.RLock()
	if rec := c.cache.cache[offset]; rec != nil {
		c.cache.lock.RUnlock()
		c.stats.incRecordsRead(1)
		c.stats.incCacheHits(1)
		return rec, nil
	}
	c.cache.lock.RUnlock()
	recordHeaderBytes := [recordHeaderSize]byte{}
	n, err := c.readAt(recordHeaderBytes[:], offset)
	if err != nil && n != recordHeaderSize {
		return nil, fmt.Errorf("lm2: partial read (%s)", err)
	}
	header := recordHeader{}
	err = binary.Read(bytes.NewReader(recordHeaderBytes[:]), binary.LittleEndian, &header)
	if err != nil {
		return nil, err
	}
	keyValBuf := make([]byte, int(header.KeyLen)+int(header.ValLen))
	n, err = c.readAt(keyValBuf, offset+recordHeaderSize)
	if err != nil && n != len(keyValBuf) {
		return nil, fmt.Errorf("lm2: partial read (%s)", err)
	}
	key := string(keyValBuf[:int(header.KeyLen)])
	value := string(keyValBuf[int(header.KeyLen):])
	rec := &record{
		recordHeader: header,
		Offset:       offset,
		Key:          key,
		Value:        value,
	}
	c.stats.incRecordsRead(1)
	c.stats.incCacheMisses(1)
	c.cache.push(rec)
	return rec, nil
}
func (c *Collection) nextRecord(rec *record, level int, dirty bool) (*record, error) {
	if rec == nil {
		return nil, errors.New("lm2: invalid record")
	}
	if atomic.LoadInt64(&rec.Next[level]) == 0 {
		return nil, nil
	}
	nextRec, err := c.readRecord(atomic.LoadInt64(&rec.Next[level]), dirty)
	if err != nil {
		return nil, err
	}
	return nextRec, nil
}