| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- package needle_map
- import (
- "sort"
- "sync"
- . "github.com/seaweedfs/seaweedfs/weed/storage/types"
- new_map "github.com/seaweedfs/seaweedfs/weed/storage/needle_map"
- )
- const (
- MaxSectionBucketSize = 1024 * 8
- LookBackWindowSize = 1024 // how many entries to look back when inserting into a section
- )
- type SectionalNeedleId uint32
- const SectionalNeedleIdLimit = 1<<32 - 1
- type SectionalNeedleValue struct {
- Key SectionalNeedleId
- OffsetLower OffsetLower `comment:"Volume offset"` //since aligned to 8 bytes, range is 4G*8=32G
- Size Size `comment:"Size of the data portion"`
- OffsetHigher OffsetHigher
- }
- type CompactSection struct {
- sync.RWMutex
- values []SectionalNeedleValue
- overflow Overflow
- start NeedleId
- end NeedleId
- }
- type Overflow []SectionalNeedleValue
- func NewCompactSection(start NeedleId) *CompactSection {
- return &CompactSection{
- values: make([]SectionalNeedleValue, 0),
- overflow: Overflow(make([]SectionalNeedleValue, 0)),
- start: start,
- }
- }
- // return old entry size
- func (cs *CompactSection) Set(key NeedleId, offset Offset, size Size) (oldOffset Offset, oldSize Size) {
- cs.Lock()
- defer cs.Unlock()
- if key > cs.end {
- cs.end = key
- }
- skey := SectionalNeedleId(key - cs.start)
- if i := cs.binarySearchValues(skey); i >= 0 {
- // update
- oldOffset.OffsetHigher, oldOffset.OffsetLower, oldSize = cs.values[i].OffsetHigher, cs.values[i].OffsetLower, cs.values[i].Size
- cs.values[i].OffsetHigher, cs.values[i].OffsetLower, cs.values[i].Size = offset.OffsetHigher, offset.OffsetLower, size
- return
- }
- var lkey SectionalNeedleId
- if len(cs.values) > 0 {
- lkey = cs.values[len(cs.values)-1].Key
- }
- hasAdded := false
- switch {
- case len(cs.values) < MaxSectionBucketSize && lkey <= skey:
- // non-overflow insert
- cs.values = append(cs.values, SectionalNeedleValue{
- Key: skey,
- OffsetLower: offset.OffsetLower,
- Size: size,
- OffsetHigher: offset.OffsetHigher,
- })
- hasAdded = true
- case len(cs.values) < MaxSectionBucketSize:
- // still has capacity and only partially out of order
- lookBackIndex := len(cs.values) - LookBackWindowSize
- if lookBackIndex < 0 {
- lookBackIndex = 0
- }
- if cs.values[lookBackIndex].Key <= skey {
- for ; lookBackIndex < len(cs.values); lookBackIndex++ {
- if cs.values[lookBackIndex].Key >= skey {
- break
- }
- }
- cs.values = append(cs.values, SectionalNeedleValue{})
- copy(cs.values[lookBackIndex+1:], cs.values[lookBackIndex:])
- cs.values[lookBackIndex].Key, cs.values[lookBackIndex].Size = skey, size
- cs.values[lookBackIndex].OffsetLower, cs.values[lookBackIndex].OffsetHigher = offset.OffsetLower, offset.OffsetHigher
- hasAdded = true
- }
- }
- // overflow insert
- if !hasAdded {
- if oldValue, found := cs.findOverflowEntry(skey); found {
- oldOffset.OffsetHigher, oldOffset.OffsetLower, oldSize = oldValue.OffsetHigher, oldValue.OffsetLower, oldValue.Size
- }
- cs.setOverflowEntry(skey, offset, size)
- } else {
- // if we maxed out our values bucket, pin its capacity to minimize memory usage
- if len(cs.values) == MaxSectionBucketSize {
- bucket := make([]SectionalNeedleValue, len(cs.values))
- copy(bucket, cs.values)
- cs.values = bucket
- }
- }
- return
- }
- func (cs *CompactSection) setOverflowEntry(skey SectionalNeedleId, offset Offset, size Size) {
- needleValue := SectionalNeedleValue{Key: skey, OffsetLower: offset.OffsetLower, Size: size, OffsetHigher: offset.OffsetHigher}
- insertCandidate := sort.Search(len(cs.overflow), func(i int) bool {
- return cs.overflow[i].Key >= needleValue.Key
- })
- if insertCandidate != len(cs.overflow) && cs.overflow[insertCandidate].Key == needleValue.Key {
- cs.overflow[insertCandidate] = needleValue
- return
- }
- cs.overflow = append(cs.overflow, SectionalNeedleValue{})
- copy(cs.overflow[insertCandidate+1:], cs.overflow[insertCandidate:])
- cs.overflow[insertCandidate] = needleValue
- }
- func (cs *CompactSection) findOverflowEntry(key SectionalNeedleId) (nv SectionalNeedleValue, found bool) {
- foundCandidate := sort.Search(len(cs.overflow), func(i int) bool {
- return cs.overflow[i].Key >= key
- })
- if foundCandidate != len(cs.overflow) && cs.overflow[foundCandidate].Key == key {
- return cs.overflow[foundCandidate], true
- }
- return nv, false
- }
- func (cs *CompactSection) deleteOverflowEntry(key SectionalNeedleId) {
- length := len(cs.overflow)
- deleteCandidate := sort.Search(length, func(i int) bool {
- return cs.overflow[i].Key >= key
- })
- if deleteCandidate != length && cs.overflow[deleteCandidate].Key == key {
- if cs.overflow[deleteCandidate].Size.IsValid() {
- cs.overflow[deleteCandidate].Size = -cs.overflow[deleteCandidate].Size
- }
- }
- }
- // return old entry size
- func (cs *CompactSection) Delete(key NeedleId) Size {
- cs.Lock()
- defer cs.Unlock()
- ret := Size(0)
- if key > cs.end {
- return ret
- }
- skey := SectionalNeedleId(key - cs.start)
- if i := cs.binarySearchValues(skey); i >= 0 {
- if cs.values[i].Size > 0 && cs.values[i].Size.IsValid() {
- ret = cs.values[i].Size
- cs.values[i].Size = -cs.values[i].Size
- }
- }
- if v, found := cs.findOverflowEntry(skey); found {
- cs.deleteOverflowEntry(skey)
- ret = v.Size
- }
- return ret
- }
- func (cs *CompactSection) Get(key NeedleId) (*new_map.NeedleValue, bool) {
- cs.RLock()
- defer cs.RUnlock()
- if key > cs.end {
- return nil, false
- }
- skey := SectionalNeedleId(key - cs.start)
- if v, ok := cs.findOverflowEntry(skey); ok {
- nv := toNeedleValue(v, cs)
- return &nv, true
- }
- if i := cs.binarySearchValues(skey); i >= 0 {
- nv := toNeedleValue(cs.values[i], cs)
- return &nv, true
- }
- return nil, false
- }
- func (cs *CompactSection) binarySearchValues(key SectionalNeedleId) int {
- x := sort.Search(len(cs.values), func(i int) bool {
- return cs.values[i].Key >= key
- })
- if x >= len(cs.values) {
- return -1
- }
- if cs.values[x].Key > key {
- return -2
- }
- return x
- }
- // This map assumes mostly inserting increasing keys
- // This map assumes mostly inserting increasing keys
- type CompactMap struct {
- list []*CompactSection
- }
- func NewCompactMap() *CompactMap {
- return &CompactMap{}
- }
- func (cm *CompactMap) Set(key NeedleId, offset Offset, size Size) (oldOffset Offset, oldSize Size) {
- x := cm.binarySearchCompactSection(key)
- if x < 0 || (key-cm.list[x].start) > SectionalNeedleIdLimit {
- // println(x, "adding to existing", len(cm.list), "sections, starting", key)
- cs := NewCompactSection(key)
- cm.list = append(cm.list, cs)
- x = len(cm.list) - 1
- //keep compact section sorted by start
- for x >= 0 {
- if x > 0 && cm.list[x-1].start > key {
- cm.list[x] = cm.list[x-1]
- // println("shift", x, "start", cs.start, "to", x-1)
- x = x - 1
- } else {
- cm.list[x] = cs
- // println("cs", x, "start", cs.start)
- break
- }
- }
- }
- // println(key, "set to section[", x, "].start", cm.list[x].start)
- return cm.list[x].Set(key, offset, size)
- }
- func (cm *CompactMap) Delete(key NeedleId) Size {
- x := cm.binarySearchCompactSection(key)
- if x < 0 {
- return Size(0)
- }
- return cm.list[x].Delete(key)
- }
- func (cm *CompactMap) Get(key NeedleId) (*new_map.NeedleValue, bool) {
- x := cm.binarySearchCompactSection(key)
- if x < 0 {
- return nil, false
- }
- return cm.list[x].Get(key)
- }
- func (cm *CompactMap) binarySearchCompactSection(key NeedleId) int {
- l, h := 0, len(cm.list)-1
- if h < 0 {
- return -5
- }
- if cm.list[h].start <= key {
- if len(cm.list[h].values) < MaxSectionBucketSize || key <= cm.list[h].end {
- return h
- }
- return -4
- }
- for l <= h {
- m := (l + h) / 2
- if key < cm.list[m].start {
- h = m - 1
- } else { // cm.list[m].start <= key
- if cm.list[m+1].start <= key {
- l = m + 1
- } else {
- return m
- }
- }
- }
- return -3
- }
- // Visit visits all entries or stop if any error when visiting
- func (cm *CompactMap) AscendingVisit(visit func(new_map.NeedleValue) error) error {
- for _, cs := range cm.list {
- cs.RLock()
- var i, j int
- for i, j = 0, 0; i < len(cs.overflow) && j < len(cs.values); {
- if cs.overflow[i].Key < cs.values[j].Key {
- if err := visit(toNeedleValue(cs.overflow[i], cs)); err != nil {
- cs.RUnlock()
- return err
- }
- i++
- } else if cs.overflow[i].Key == cs.values[j].Key {
- j++
- } else {
- if err := visit(toNeedleValue(cs.values[j], cs)); err != nil {
- cs.RUnlock()
- return err
- }
- j++
- }
- }
- for ; i < len(cs.overflow); i++ {
- if err := visit(toNeedleValue(cs.overflow[i], cs)); err != nil {
- cs.RUnlock()
- return err
- }
- }
- for ; j < len(cs.values); j++ {
- if err := visit(toNeedleValue(cs.values[j], cs)); err != nil {
- cs.RUnlock()
- return err
- }
- }
- cs.RUnlock()
- }
- return nil
- }
- func toNeedleValue(snv SectionalNeedleValue, cs *CompactSection) new_map.NeedleValue {
- offset := Offset{
- OffsetHigher: snv.OffsetHigher,
- OffsetLower: snv.OffsetLower,
- }
- return new_map.NeedleValue{Key: NeedleId(snv.Key) + cs.start, Offset: offset, Size: snv.Size}
- }
- func toSectionalNeedleValue(nv new_map.NeedleValue, cs *CompactSection) SectionalNeedleValue {
- return SectionalNeedleValue{
- Key: SectionalNeedleId(nv.Key - cs.start),
- OffsetLower: nv.Offset.OffsetLower,
- Size: nv.Size,
- OffsetHigher: nv.Offset.OffsetHigher,
- }
- }
|