boutique

package module
v0.1.1-beta.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2017 License: MIT Imports: 9 Imported by: 18

Documentation

Overview

Package boutique provides an immutable state storage with subscriptions to changes in the store. It is intended to be a single storage for individual client data or a single state store for an application not requiring high QPS.

Features and Drawbacks

Features:

  • Immutable data does not require locking outside the store.
  • Subscribing to individual field changes are simple.
  • Data locking is handled by the Store.

Drawbacks:

  • You are giving up static type checks on compile.
  • Internal reflection adds about 1.8x overhead.
  • Must be careful to not mutate data.

Immutability

When we say immutable, we mean that everything gets copied, as Go does not have immutable objects or types other than strings. This means every update to a pointer or reference type (map, dict, slice) must make a copy of the data before changing it, not a mutation. Because of modern processors, this copying is quite fast.

Usage structure

Boutique provides storage that is best designed in a modular method:

  └── state
    ├── state.go
    ├── actions
    │   └── actions.go
		├── data
		|   └── data.go
		├── middleware
		|   └── middleware.go
    └── modifiers
        └── modifiers.go

The files are best organized by using them as follows:

  state.go - Holds the constructor for a boutique.Store for your application
  actions.go - Holds the actions that will be used by the updaters to update the store
	data.go - Holds the definition of your state object
	middleware.go = Holds middleware for acting on proposed changes to your data. This is not required
  modifiers.go - Holds all the Modifier(s) that are used by the boutique.Store to modify the store's data

  Note: These are all simply suggestions, you can combine this in a single file or name the files whatever you wish.

Example

Please see github.com/johnsiilver/boutique for a complete guide to using this package. Its complicated enough to warrant some documentation to guide you through.

If your very impatient, there is an example directory with examples of verying complexity.

Index

Constants

View Source
const Any = "any"

Any is used to indicated to Store.Subscribe() that you want updates for any update to the store, not just a field.

Variables

This section is empty.

Functions

func CopyAppendSlice

func CopyAppendSlice(slice interface{}, item interface{}) interface{}

CopyAppendSlice takes a slice, copies the slice into a new slice and appends item to the new slice. If slice is not actually a slice or item is not the same type as []Type, then this will panic. This is simply a convenience function for copying then appending to a slice. It is faster to do this by hand without the reflection. This is also not a deep copy, its simply copies the underlying array.

func DeepCopy

func DeepCopy(from, to interface{}) error

DeepCopy makes a DeepCopy of all elements in "from" into "to". "to" must be a pointer to the type of "from". "from" and "to" must be the same type. Private fields will not be copied. It this is needed, you should handle the copy method yourself.

func ShallowCopy

func ShallowCopy(i interface{}) interface{}

ShallowCopy makes a copy of a value. On pointers or references, you will get a copy of the pointer, not of the underlying value.

Types

type Action

type Action struct {
	// Type should be an enumerated constant representing the type of Action.
	// It is valuable to use http://golang.org/x/tools/cmd/stringer to allow
	// for string representation.
	Type ActionType

	// Update holds the values to alter in the Update.
	Update interface{}
}

Action represents an action to take on the Store.

type ActionType

type ActionType int

ActionType indicates what type of Action this is.

type CancelFunc

type CancelFunc func()

CancelFunc is used to cancel a subscription

type GetState

type GetState func() State

GetState returns the state of the Store.

type MWArgs

type MWArgs struct {
	// Action is the Action that is being performed.
	Action Action
	// NewData is the proposed new State.Data field in the Store. This can be modified by the
	// Middleware and returned as the changedData return value.
	NewData interface{}
	// GetState if a function that will return the current State of the Store.
	GetState GetState
	// Committed is only used if the Middleware will spin off a goroutine.  In that case,
	// the committed state will be sent via this channel. This allows Middleware that wants
	// to do something based on the final state (like logging) to work.  If the data was not
	// committed due to another Middleware cancelling the commit, State.IsZero() will be true.
	Committed chan State

	// WG must have .Done() called by all Middleware once it has finished. If using Committed, you must
	// not call WG.Done() until your goroutine is completed.
	WG *sync.WaitGroup
}

MWArgs are the arguments to a Middleware implmentor.

type Middleware

type Middleware func(args *MWArgs) (changedData interface{}, stop bool, err error)

Middleware provides a function that is called before the state is written. The Action that is being applied is passed, with the newData that is going to be commited, a method to get the current state, and committed which will close when newData is committed. It returns either a changed version of newData or nil if newData is unchanged. It returns an indicator if we should stop processing middleware but continue with our commit of the newData. And it returns an error if we should not commit. Finally the "wg" WaitGroup that is passed must have .Done() called when the Middleware finishes. "committed" can be ignored unless the middleware wants to spin off a goroutine that does something after the data is committed. If the data is not committed because another Middleware returns an error, the channel will be closed with an empty state. This ability allow Middleware that performs things such as logging the final result. If using this ability, do not call wg.Done() until all processing is done.

type Modifier

type Modifier func(state interface{}, action Action) interface{}

Modifier takes in the existing state and an action to perform on the state. The result will be the new state. Implementation of an Modifier must be careful to not mutate "state", it must work on a copy only. If you are changing a reference type contained in state, you must make a copy of that reference first and then manipulate the copy, storing it in the new state object.

type Modifiers

type Modifiers struct {
	// contains filtered or unexported fields
}

Modifiers provides the internals the ability to use the Modifier.

func NewModifiers

func NewModifiers(modifiers ...Modifier) Modifiers

NewModifiers creates a new Modifiers with the Modifiers provided.

type PerformOption

type PerformOption func(p *performOptions)

PerformOption is an optional arguement to Store.Peform() calls.

func NoUpdate

func NoUpdate() PerformOption

NoUpdate indicates that when this Perform() is run, no subscribers affected should receive an update for this change.

func WaitForCommit

func WaitForCommit(wg *sync.WaitGroup) PerformOption

WaitForCommit passed a WaitGroup that will be decremented by 1 when a Perform is completed. This option allows you to use goroutines to call Perform, continue on and then wait for the commit to be completed. Because you cannot increment the WaitGroup before the Perform, you must wait until Peform() is completed. If doing Perform in a

func WaitForSubscribers

func WaitForSubscribers(ch chan State) PerformOption

WaitForSubscribers passes a channel that will receive the State from a change once all subscribers have been updated with this state. This channel should generally have a buffer of 1. If not, the Perform() will return an error. If the channel is full when it tries to update the channel, no update will be sent.

type Signal

type Signal struct {
	// Version is the version of the field that was changed.  If Any was passed, it will
	// be the store's version, not a specific field.
	Version uint64

	// Fields are the field names that were updated.  This is only a single name unless
	// Any is used.
	Fields []string

	// State is the new State object.
	State State
}

Signal is used to signal upstream subscribers that a field in the Store.Store has changed.

func (Signal) FieldChanged

func (s Signal) FieldChanged(f string) bool

FieldChanged loops over Fields to deterimine if "f" exists. len(s.Fields) is always small, so a linear search is optimal. Only useful if you are subscribed to "Any", as otherwise its a single entry.

type State

type State struct {
	// Version is the version of the state this represents.  Each change updates
	// this version number.
	Version uint64

	// FieldVersions holds the version each field is at. This allows us to track
	// individual field updates.
	FieldVersions map[string]uint64

	// Data is the state data.  They type is some type of struct.
	Data interface{}
}

State holds the state data.

func (State) IsZero

func (s State) IsZero() bool

IsZero indicates that the State isn't set.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store provides access to the single data store for the application. The Store is thread-safe.

func New

func New(initialState interface{}, mod Modifiers, middle []Middleware) (*Store, error)

New is the constructor for Store. initialState should be a struct that is used for application's state. All Modifiers in mod must return the same struct that initialState contains or you will receive a panic.

func (*Store) FieldVersion

func (s *Store) FieldVersion(f string) uint64

FieldVersion returns the current version of field "f". If "f" doesn't exist, the default value 0 will be returned.

func (*Store) Perform

func (s *Store) Perform(a Action, options ...PerformOption) error

Perform performs an Action on the Store's state.

func (*Store) State

func (s *Store) State() State

State returns the current stored state.

func (*Store) Subscribe

func (s *Store) Subscribe(field string) (chan Signal, CancelFunc, error)

Subscribe creates a subscriber to be notified when a field is updated. The notification comes over the returned channel. If the field is set to the Any enumerator, any field change in the state data sends an update. CancelFunc() can be called to cancel the subscription. On cancel, chan Signal will be closed after the last entry is pulled from the channel. The returned channel is guarenteed to have the latest data at the time the Signal is returned. If you pull off the channel slower than the sender, you will still receive the latest data when you pull off the channel. It is not guarenteed that you will see every update, only the latest. If you need every update, you need to write middleware.

func (*Store) Version

func (s *Store) Version() uint64

Version returns the current version of the Store.

Directories

Path Synopsis
example
basic
This program is a simplistic example of using Boutique as a state store.
This program is a simplistic example of using Boutique as a state store.
chatterbox/client
Package client provides a ChatterBox client for communicating with a ChatterBox server.
Package client provides a ChatterBox client for communicating with a ChatterBox server.
chatterbox/messages
Package messages holds the client/erver messages that are sent on the write in JSON format.
Package messages holds the client/erver messages that are sent on the write in JSON format.
chatterbox/server
Package server implements a websocket server that sets up an irc like server.
Package server implements a websocket server that sets up an irc like server.
chatterbox/server/state
Package state contains our Hub, which is used to store data for a particular channel users are communicating on.
Package state contains our Hub, which is used to store data for a particular channel users are communicating on.
chatterbox/server/state/actions
Package actions details boutique.Actions that are used by modifiers to modify the store.
Package actions details boutique.Actions that are used by modifiers to modify the store.
chatterbox/server/state/data
Package data holds the Store object that is used by our boutique instances.
Package data holds the Store object that is used by our boutique instances.
chatterbox/server/state/middleware
Package middleware provides middleware to our boutique.Container.
Package middleware provides middleware to our boutique.Container.
chatterbox/server/state/modifiers
Package modifiers holds all the boutique.Updaters and the boutique.Modifer for the state store.
Package modifiers holds all the boutique.Updaters and the boutique.Modifer for the state store.
notifier/state
Package state contains our Hub, which is used to store data for a particular channel users are communicating on.
Package state contains our Hub, which is used to store data for a particular channel users are communicating on.
notifier/state/actions
Package actions details boutique.Actions that are used by modifiers to modify the store.
Package actions details boutique.Actions that are used by modifiers to modify the store.
notifier/state/data
Package data holds the Store object that is used by our boutique instances.
Package data holds the Store object that is used by our boutique instances.
notifier/state/modifiers
Package modifiers holds all the boutique.Updaters and the boutique.Modifer for the state store.
Package modifiers holds all the boutique.Updaters and the boutique.Modifer for the state store.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL