package actor import ( "bufio" "encoding/json" "errors" "io" "time" "google.golang.org/protobuf/proto" ) type StateStorage struct { registry MutationRegistry // metrics is set by the owning DiskStorage via SetMetrics. Nil is fine — // every method that touches it nil-checks first. The pointer is // intentionally unexported: callers set it through DiskStorage.SetMetrics // so the embedded StateStorage always tracks the parent disk storage's // metrics, even if the embedded value is replaced. metrics *Metrics } type StorageEvent struct { Type string `json:"type"` TimeStamp time.Time `json:"timestamp"` Mutation proto.Message `json:"mutation"` } type rawEvent struct { Type string `json:"type"` TimeStamp time.Time `json:"timestamp"` Mutation json.RawMessage `json:"mutation"` } func NewState(registry MutationRegistry) *StateStorage { return &StateStorage{ registry: registry, } } var ErrUnknownType = errors.New("unknown type") func (s *StateStorage) Load(r io.Reader, onMessage func(msg proto.Message, timeStamp time.Time)) error { var err error var evt *StorageEvent scanner := bufio.NewScanner(r) for err == nil { evt, err = s.Read(scanner) if err == nil { onMessage(evt.Mutation, evt.TimeStamp) } } if err == io.EOF { return nil } return err } // Append serialises a mutation as a StorageEvent and writes it (plus a // trailing newline) to io. The first return value is the number of // bytes written, which the caller (DiskStorage) adds to the // event_log_bytes_written_total counter; the second is the underlying // write error, if any. ErrUnknownType is returned (with 0 bytes) when // the mutation's type is not registered, and is also counted in // event_log_unknown_types_total. func (s *StateStorage) Append(io io.Writer, mutation proto.Message, timeStamp time.Time) (int, error) { typeName, ok := s.registry.GetTypeName(mutation) if !ok { if s.metrics != nil { s.metrics.EventLogUnknownTypes.Inc() } return 0, ErrUnknownType } event := &StorageEvent{ Type: typeName, TimeStamp: timeStamp, Mutation: mutation, } jsonBytes, err := json.Marshal(event) if err != nil { return 0, err } n, err := io.Write(jsonBytes) total := n if err != nil { return total, err } n, err = io.Write([]byte("\n")) total += n return total, err } func (s *StateStorage) Read(r *bufio.Scanner) (*StorageEvent, error) { var event rawEvent if r.Scan() { b := r.Bytes() err := json.Unmarshal(b, &event) if err != nil { return nil, err } typeName := event.Type mutation, ok := s.registry.Create(typeName) if !ok { if s.metrics != nil { s.metrics.EventLogUnknownTypes.Inc() } return nil, ErrUnknownType } if err := json.Unmarshal(event.Mutation, mutation); err != nil { return nil, err } return &StorageEvent{ Type: typeName, TimeStamp: event.TimeStamp, Mutation: mutation, }, r.Err() } return nil, io.EOF }