2023-10-13 09:43:27 +00:00
|
|
|
package mdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"sync/atomic"
|
2023-10-16 08:50:19 +00:00
|
|
|
|
|
|
|
"git.crumpington.com/public/jldb/mdb/change"
|
2023-10-13 09:43:27 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Snapshot struct {
|
|
|
|
parent atomic.Pointer[Snapshot]
|
|
|
|
// The Snapshot's version is incremented each time it's cloned.
|
|
|
|
version uint64
|
|
|
|
|
|
|
|
// The snapshot's seqNum is set when it becomes active (read-only).]
|
|
|
|
seqNum int64
|
|
|
|
timestampMS int64
|
|
|
|
|
|
|
|
collections map[uint64]any // Map from collection ID to *collectionState[T].
|
|
|
|
changes []change.Change
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSnapshot() *Snapshot {
|
|
|
|
return &Snapshot{
|
|
|
|
collections: map[uint64]any{},
|
|
|
|
changes: []change.Change{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) addCollection(id uint64, c any) {
|
|
|
|
s.collections[id] = c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) writable() bool {
|
|
|
|
return s.parent.Load() != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) setReadOnly() {
|
|
|
|
s.parent.Store(nil)
|
|
|
|
s.changes = s.changes[:0]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) store(cID, iID uint64, item any) {
|
|
|
|
change := s.appendChange(cID, iID)
|
|
|
|
change.Store = true
|
|
|
|
|
|
|
|
buf := bytes.NewBuffer(change.Data[:0])
|
|
|
|
if err := json.NewEncoder(buf).Encode(item); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
change.Data = buf.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) delete(cID, iID uint64) {
|
|
|
|
change := s.appendChange(cID, iID)
|
|
|
|
change.Store = false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) appendChange(cID, iID uint64) *change.Change {
|
|
|
|
if len(s.changes) == cap(s.changes) {
|
|
|
|
s.changes = append(s.changes, change.Change{})
|
|
|
|
} else {
|
|
|
|
s.changes = s.changes[:len(s.changes)+1]
|
|
|
|
}
|
|
|
|
|
|
|
|
change := &s.changes[len(s.changes)-1]
|
|
|
|
change.CollectionID = cID
|
|
|
|
change.ItemID = iID
|
|
|
|
change.Store = false
|
|
|
|
change.ClearPageIDs = change.ClearPageIDs[:0]
|
|
|
|
change.WritePageIDs = change.WritePageIDs[:0]
|
|
|
|
change.Data = change.Data[:0]
|
|
|
|
return change
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) begin() *Snapshot {
|
|
|
|
c := s.clone()
|
|
|
|
c.changes = c.changes[:0]
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) clone() *Snapshot {
|
|
|
|
collections := make(map[uint64]any, len(s.collections))
|
|
|
|
for k, v := range s.collections {
|
|
|
|
collections[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
c := &Snapshot{
|
|
|
|
version: s.version + 1,
|
|
|
|
collections: collections,
|
|
|
|
changes: s.changes[:],
|
|
|
|
}
|
|
|
|
c.parent.Store(s)
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Snapshot) rollback() *Snapshot {
|
|
|
|
parent := s.parent.Load()
|
|
|
|
if parent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't throw away allocated changes.
|
|
|
|
parent.changes = s.changes[:len(parent.changes)]
|
|
|
|
return parent
|
|
|
|
}
|