| package state |
| |
| import ( |
| "time" |
| |
| "veyron/services/store/memstore/refs" |
| |
| "veyron2/security" |
| "veyron2/storage" |
| ) |
| |
| type State struct { |
| snapshot *MutableSnapshot |
| |
| // timestamp is the time of the last mutation applied, in nanoseconds since |
| // the epoch. See comment for snapshot.Mutations.Timestamp. |
| timestamp uint64 |
| } |
| |
| // refUpdate represents a reference change to a value. |
| type refUpdate struct { |
| id storage.ID |
| before refs.Set |
| after refs.Set |
| } |
| |
| // New returns an empty State. |
| func New(admin security.PublicID) *State { |
| return &State{snapshot: newMutableSnapshot(admin)} |
| } |
| |
| // Timestamp returns the timestamp of the latest mutation to the state in |
| // nanoseconds. |
| func (st *State) Timestamp() uint64 { |
| return st.timestamp |
| } |
| |
| // DeepCopy creates a copy of the state. Mutations to the copy do not affect |
| // the original, and vice versa. |
| func (st *State) DeepCopy() *State { |
| return &State{st.MutableSnapshot(), st.timestamp} |
| } |
| |
| // GC performs a manual garbage collection. |
| func (st *State) GC() { |
| st.snapshot.gc() |
| } |
| |
| // Snapshot returns a read-only copy of the state. |
| func (st *State) Snapshot() Snapshot { |
| return st.snapshot.GetSnapshot() |
| } |
| |
| // MutableSnapshot creates a copy of the state. Mutations to the copy do not |
| // affect the original, and vice versa. |
| func (st *State) MutableSnapshot() *MutableSnapshot { |
| return st.snapshot.deepCopy() |
| } |
| |
| // Deletions returns the set of IDs for values that have been deleted from |
| // the state. Returns nil iff there have been no deletions. |
| func (st *State) Deletions() *Mutations { |
| if len(st.snapshot.deletions) == 0 { |
| return nil |
| } |
| |
| // Package the deletions into a transaction. |
| var mu Mutations |
| ts := st.timestamp + 1 |
| mu.Timestamp = ts |
| mu.Deletions = st.snapshot.deletions |
| st.timestamp = ts |
| st.snapshot.deletions = make(map[storage.ID]storage.Version) |
| return &mu |
| } |
| |
| // ApplyMutations applies a set of mutations atomically. |
| // |
| // We don't need to check permissions because: |
| // 1. Permissions were checked as the mutations were created. |
| // 2. Preconditions ensure that all paths to modified values haven't changed. |
| // 3. The client cannot fabricate a mutations value. |
| func (st *State) ApplyMutations(mu *Mutations) error { |
| // Assign a timestamp. |
| ts := uint64(time.Now().UnixNano()) |
| if ts <= st.timestamp { |
| ts = st.timestamp + 1 |
| } |
| mu.Timestamp = ts |
| |
| if err := st.snapshot.applyMutations(mu); err != nil { |
| return err |
| } |
| |
| st.timestamp = ts |
| return nil |
| } |
| |
| func (sn *MutableSnapshot) applyMutations(mu *Mutations) error { |
| // Check the preconditions. |
| table := sn.idTable |
| for id, pre := range mu.Preconditions { |
| c, ok := table.Get(&Cell{ID: id}) |
| // If the precondition is 0, it means that the cell is being created, |
| // and it must not already exist. We get a precondition failure if pre |
| // is 0 and the cell already exists, or pre is not 0 and the cell does |
| // not already exist or have the expected version. |
| if pre == 0 && ok || pre != 0 && (!ok || c.(*Cell).Version != pre) { |
| return errPreconditionFailed |
| } |
| } |
| for id, pre := range mu.Deletions { |
| c, ok := table.Get(&Cell{ID: id}) |
| // The target is not required to exist. |
| if ok && c.(*Cell).Version != pre { |
| return errPreconditionFailed |
| } |
| } |
| |
| // Changes to the state begin now. These changes should not fail, |
| // as we don't support rollback. |
| |
| // Apply the mutations. |
| updates := make([]*refUpdate, 0, len(mu.Delta)) |
| for id, m := range mu.Delta { |
| d := refs.BuildDir(m.Dir) |
| cl, ok := table.Get(&Cell{ID: id}) |
| sn.aclCache.Invalidate(id) |
| if !ok { |
| c := &Cell{ |
| ID: id, |
| Version: m.Postcondition, |
| Value: m.Value, |
| Dir: d, |
| Tags: m.Tags, |
| refs: m.refs, |
| inRefs: refs.Empty, |
| } |
| table = table.Put(c) |
| updates = append(updates, &refUpdate{id: c.ID, before: refs.Empty, after: c.refs}) |
| } else { |
| c := cl.(*Cell) |
| cp := *c |
| cp.Version = m.Postcondition |
| cp.Value = m.Value |
| cp.Dir = d |
| cp.Tags = m.Tags |
| cp.refs = m.refs |
| table = table.Put(&cp) |
| updates = append(updates, &refUpdate{id: c.ID, before: c.refs, after: cp.refs}) |
| } |
| } |
| sn.idTable = table |
| |
| // Add the refs. |
| for _, u := range updates { |
| sn.updateRefs(u.id, u.before, u.after) |
| } |
| |
| // Redirect the rootID. |
| if mu.SetRootID && mu.RootID != sn.rootID { |
| if mu.RootID != nullID { |
| sn.ref(mu.RootID) |
| } |
| if sn.rootID != nullID { |
| sn.unref(sn.rootID) |
| } |
| sn.rootID = mu.RootID |
| } |
| |
| return nil |
| } |