whatnot

package module
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2021 License: BSD-2-Clause Imports: 12 Imported by: 1

README

Whatnot - an Etcd-like Distributed Locking Namespace Manager

Go Codacy Badge Go Report Card

If you like Etcd's namespace-driven notification subscription model, but have no requirements for long-term storage, then the formality of Etcetera can be dropped in favor of... Whatnot.

Whatnot is An Etcd-like Lockable Namespace Manager, allowing you to define a hierarchical namespace of keys (which may contain values), that can be locked with an expiring lease, either individually or for an entire prefix of the key path tree.

Performance is key, everything is done in-memory with no backing store or persistence. Multiple instances of your application can synchronize the data, so that persistence is perpetual so long as a single instance remains online.

Leases and Watch notifications occur in much the same way that they do with Etcd and its Go client. Set a watch on a particular element of a namespaced path and recieve notifications of events to that element, or optionally, any element beneath it. Set a lease on a path element and receieve an expiring mutex on modifications to it, and optionally any of its sub-paths.

Suggested Use Cases

Originally inspired by the Kubernetes REST API and its hierarchical ordering of resources, I wanted something similar, but with lower latency, and without storing the full resources into the system itself. I do a lot of microservices development and a key design factor in those is being able to extend out the datamodel, without having to backport it to existing, stable components every time.

Etcd's capability to arrange keys in a directory-like namespace, and apply mutex-style leases to a both a single key and its descendents appealed to me for this reason - allowing other components to extend a resource and be notified of it without other components having to know of these items specifically. Etcd's ability to subscribe ('watch') a key, and send notifications to subscribers of leases and changes to a single key or all of its children as well, was immensely useful for this design pattern.

Caching and Resource Locking are two tasks that are almost inseparable, and this is where Whatnot is intended to excel - acting as a distributed state coordinator, especially for microservice-based application architectures, where multiple elastically-scaling instances of a service exist together in coordinated deployments, and the overhead of an external service (redis, etc) to provide this functionality is an unnecessary resource overhead when all is required from these services is the coordination of state.

Why use this over [ProjectName]
Over Etcd ?

Simply put, Etcd's storage quorum functionality placed an upper limit on the number of notifications per second, and their accumulated latency, causing some notifications to be placed into a 'get around to eventually' queue. Those limitations combined with the resource requirements of Etcd were what inspired this project in the first place.

Over Redis ?

Redis gives tools to build some complex setups that resemble this, and it has close to the required speed too, but they require use of advanced commands and data modelling to approximate the same functionality. All of this places Redis on a scale larger than what I required here.

Over Consul ?

Consul, like Etcd, has the same hierarchical keyspace support, but focuses on persisting data with fairly low levels of state changes.

Discouraged Use cases

Whatnot was created with the intention of creating a specialized package that incorporates certain features of larger, more popular software projects, without the unwanted overhead of their additional featureset - hence.

Whatnot is not intended for persisting data.

Although its possible to write your own serializer to save the state of a Whatnot namespace (perhaps during application shutdown) the internal system itself is not intended to incorporate any kind of storage backend - Other software accomplishes this requirement and use pattern far better already. So although Whatnot includes support for values on keys, those values should never be the only canonical store of that value.

  • Speed is always in contention to Accuracy
  • Extremely large numbers of keys

Key Functionality

Individual Namespaces
  • Completely separate notification channels
Native Hierarchy Support

Just as with Etcd, Keys are organized into directory-like tree structures, where every key can have sub-keys.

Just as with directory paths, this gives us the concept of both Absolute Paths and Relative Paths.

Leases and Prefix Leases

Returns a native Go context.Context object and cancel() function - once you have obtained a lock, the rest of your code doesnt need to care about Whatnot, just follow regular Go patterns for working within a scope that has a deadline. Leases will forcibly unlock the corresponding element once their deadline is reached, so it is up to your code to obey that channel signal and cease any further operations that could induce race conditions.

Emphasis on lightweight operation
  • No additional infrastructure
  • No storage persistence/synchronization latency
  • No huge memory requirements
  • not intended to be used for billions of key
Emphasis on Structure and Hierarchy
  • Etcd's explicit directory-path style namespaces were a huge inspiration
Decentralized
  • Support for direct clustering between your applications instances via Gossip, Raft and gRPC
Built without storage persistence and centralization in mind.
  • Designed originally to coordinate cluster cache invalidation

Resource Management

Whatnot defaults to a highly reactive concurrency model, whereby each distinctly unique element in the namespace recieves its own dedicated routine to pass up event notifications to its parent.

Documentation

Overview

Package whatnot is a low-footprint, high-speed, cluster-friendly volatile system for providing etcd-like distributed semaphores on hierarchical resource identifiers with subscribable watch notifications.

It is intended to replace the functionality of systems like Redis and Etcd, in situations where data persistence over long periods of time is not an issue, additional infrastructure is not desired, and peer-to-peer sharing of data is a preferable solution for extreme low-latency

Whatnot (a far more informal rendering of 'etcetera') was driven by a desire to utilize the namespace subscription capabilities of Etcd, without the investment in storage and memory it required to maintain persistent data, a feature I did not require at the time.

Index

Constants

View Source
const (
	MaxPathDepth  = 50
	MaxPathLength = 4096
)
View Source
const (
	ChangeUnknown changeType = iota + 1
	ChangeLocked
	ChangeUnlocked
	ChangeAdded
	ChangeEdited
	ChangeDeleted
	ChangePruned
	ChangeReleased
)

Variables

View Source
var WithAcls managerOptionFunc = func() optionName {
	return optionAcls
}

WithAcls turns on Whatnot's Access Control Management on individual Path Elements

View Source
var WithDeadlockBreak managerOptionFunc = func() optionName {
	return optionBreak
}

WithDeadlockBreak turns on Whatnot's Self-healing breaking of Mutex Deadlocks

View Source
var WithGossip managerOptionFunc = func() optionName {
	return optionDiscoverGossip
}

WithGossip enables Gossip protocol Cluster member discovery of other instances running the whatNot gRPC connector

View Source
var WithPruning managerOptionFunc = func() optionName {
	return optionAcls
}

WithPruning turns on Whatnot's automatic pruning of Unused PathElement Tree sections after they remain unused for a given amount of time

View Source
var WithRaft managerOptionFunc = func() optionName {
	return optionSyncRaft
}

WithRaft enables Raft Quorum synchromization - improving cluster accuracy at a slight speed and bandwith cost

View Source
var WithRateLimit = func() optionName {
	return optionRateLimit
}
View Source
var WithTrace managerOptionFunc = func() optionName {
	return optionTrace
}

WithTrace enables extended tracing of Resource Locking and Wait Queues

Functions

func HealthHandler added in v0.6.0

func HealthHandler(r *http.Request, w http.ResponseWriter)

HealthHandler is an HTTP HandlerFunc to attach this to your appropriate HTTP Healthcheck Endpoint

func Healthy added in v0.6.0

func Healthy() bool

Healthy provides a Global HealthCheck poller with a simple yes/no response to overall subsystem health

Types

type AbsolutePath

type AbsolutePath []SubPath

AbsolutePath is the fully-qualified path to a single PathElement from the root of the Namespace it resides in

func (AbsolutePath) SubtractPath

func (m AbsolutePath) SubtractPath(path AbsolutePath) PathString

SubtractPath removes the right-hand-size RelativePath from the AbsolutePath

func (AbsolutePath) ToPathString

func (m AbsolutePath) ToPathString() PathString

ToPathString converts an absolute path back into a delimited string

type ConfigError added in v0.7.0

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

type ElementValue

type ElementValue struct {
	Val interface{}
}

type ElementWatchSubscription

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

ElementWatchSubscription is a contract to be notified of all events on a given Path Element, and optionally all its child elements

func (ElementWatchSubscription) Debug added in v0.6.0

func (n ElementWatchSubscription) Debug(msg string)

func (ElementWatchSubscription) Debugf added in v0.6.0

func (n ElementWatchSubscription) Debugf(format string, a ...interface{})

func (ElementWatchSubscription) Error added in v0.6.0

func (n ElementWatchSubscription) Error(msg string)

func (ElementWatchSubscription) Errorf added in v0.6.0

func (n ElementWatchSubscription) Errorf(format string, a ...interface{})

func (*ElementWatchSubscription) Events

func (m *ElementWatchSubscription) Events() <-chan WatchEvent

Events returns a channel of subscriberNotify occurring to this Key (or its subKeys

func (ElementWatchSubscription) Info added in v0.6.0

func (n ElementWatchSubscription) Info(msg string)

func (ElementWatchSubscription) Infof added in v0.6.0

func (n ElementWatchSubscription) Infof(format string, a ...interface{})

func (ElementWatchSubscription) Warn added in v0.6.0

func (n ElementWatchSubscription) Warn(msg string)

func (ElementWatchSubscription) Warnf added in v0.6.0

func (n ElementWatchSubscription) Warnf(format string, a ...interface{})

type EventMultiplexer

type EventMultiplexer struct {

	// Broadcast is the channel to set events to for them to be multiplexed out
	Broadcast chan<- WatchEvent
	// contains filtered or unexported fields
}

EventMultiplexer is a pub-sub mechanism where consumers can Register to receive messages sent to Broadcast.

func NewEventsMultiplexer added in v0.6.0

func NewEventsMultiplexer() *EventMultiplexer

NewEventsMultiplexer creates a new event multiplexer that will duplicate incoming WatchEvents to multiple watcher channels

func (*EventMultiplexer) BroadcastAsync

func (t *EventMultiplexer) BroadcastAsync(evt WatchEvent)

BroadcastAsync has the multiplexer submit the WatchEvent instead of the caller attaching directly to a channel delivery is not guaranteed in this case and the goroutine will eventually exit if it deadlocks

func (EventMultiplexer) Debug added in v0.6.0

func (n EventMultiplexer) Debug(msg string)

func (EventMultiplexer) Debugf added in v0.6.0

func (n EventMultiplexer) Debugf(format string, a ...interface{})

func (EventMultiplexer) Error added in v0.6.0

func (n EventMultiplexer) Error(msg string)

func (EventMultiplexer) Errorf added in v0.6.0

func (n EventMultiplexer) Errorf(format string, a ...interface{})

func (EventMultiplexer) Info added in v0.6.0

func (n EventMultiplexer) Info(msg string)

func (EventMultiplexer) Infof added in v0.6.0

func (n EventMultiplexer) Infof(format string, a ...interface{})

func (*EventMultiplexer) Register

func (t *EventMultiplexer) Register(ch chan<- WatchEvent, recursive bool)

Register starts receiving messages on the given channel. If a channel close is seen, either the topic has been shut down, or the consumer was too slow, and should re-register.

func (*EventMultiplexer) Unregister

func (t *EventMultiplexer) Unregister(ch chan<- WatchEvent)

Unregister stops receiving messages on this channel.

func (EventMultiplexer) Warn added in v0.6.0

func (n EventMultiplexer) Warn(msg string)

func (EventMultiplexer) Warnf added in v0.6.0

func (n EventMultiplexer) Warnf(format string, a ...interface{})

type LeaseContext

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

LeaseContext implements Element Locking Lease control as a Context Interface object it is heavily recommend to use this as the context object for the rest of your functions lifetime to keep it in sync with the accordant lease it was generated with to enable your code to control and react to lease expiration

func (*LeaseContext) Cancel

func (l *LeaseContext) Cancel()

Cancel implements the Context interface

func (*LeaseContext) Deadline

func (l *LeaseContext) Deadline() (time.Time, bool)

Deadline implements the Context interface

func (LeaseContext) Debug added in v0.6.0

func (n LeaseContext) Debug(msg string)

func (LeaseContext) Debugf added in v0.6.0

func (n LeaseContext) Debugf(format string, a ...interface{})

func (*LeaseContext) Done

func (l *LeaseContext) Done() <-chan struct{}

Done implements the Context interface

func (*LeaseContext) Err

func (l *LeaseContext) Err() error

Err implements the Context interface

func (LeaseContext) Error added in v0.6.0

func (n LeaseContext) Error(msg string)

func (LeaseContext) Errorf added in v0.6.0

func (n LeaseContext) Errorf(format string, a ...interface{})

func (LeaseContext) Info added in v0.6.0

func (n LeaseContext) Info(msg string)

func (LeaseContext) Infof added in v0.6.0

func (n LeaseContext) Infof(format string, a ...interface{})

func (*LeaseContext) Value

func (l *LeaseContext) Value(key interface{}) interface{}

Value implements the Context interface

func (LeaseContext) Warn added in v0.6.0

func (n LeaseContext) Warn(msg string)

func (LeaseContext) Warnf added in v0.6.0

func (n LeaseContext) Warnf(format string, a ...interface{})

type Logger

type Logger interface {
	Debug(msg string)
	Debugf(format string, a ...interface{})
	Info(msg string)
	Infof(format string, a ...interface{})
	Warn(msg string)
	Warnf(format string, a ...interface{})
	Error(msg string)
	Errorf(format string, a ...interface{})
}

Logger allows you to implement/attach your own logger

type ManagerOption

type ManagerOption interface {
	// contains filtered or unexported methods
}

type NameSpaceManager

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

NameSpaceManager provides top-level management of unique element namespaces

func NewNamespaceManager

func NewNamespaceManager(opts ...ManagerOption) (nsm *NameSpaceManager, err error)

NewNamespaceManager create a top-level namespace manager, to contain multiple subscribable namespaces you probably only want to call this once, to initialize WhatNot, but who am I to tell you what your use cases are

func (NameSpaceManager) Debug added in v0.7.0

func (n NameSpaceManager) Debug(msg string)

func (NameSpaceManager) Debugf added in v0.7.0

func (n NameSpaceManager) Debugf(format string, a ...interface{})

func (NameSpaceManager) Error added in v0.7.0

func (n NameSpaceManager) Error(msg string)

func (NameSpaceManager) Errorf added in v0.7.0

func (n NameSpaceManager) Errorf(format string, a ...interface{})

func (*NameSpaceManager) FetchNamespace

func (m *NameSpaceManager) FetchNamespace(name string) (ns *Namespace, err error)

FetchNamespace gets you access to the requested namespace understandably most other operations involving a namespace's contents begin here

func (NameSpaceManager) Info added in v0.7.0

func (n NameSpaceManager) Info(msg string)

func (NameSpaceManager) Infof added in v0.7.0

func (n NameSpaceManager) Infof(format string, a ...interface{})

func (*NameSpaceManager) RegisterNamespace

func (m *NameSpaceManager) RegisterNamespace(ns *Namespace) error

RegisterNamespace actives a name Namespace into the list of actively available and subscribable namespaces

func (*NameSpaceManager) UnRegisterNamespace

func (m *NameSpaceManager) UnRegisterNamespace(ns *Namespace) error

UnRegisterNamespace will completely remove a given namespace all the properties, leases, subscriptions, etc within it.

func (NameSpaceManager) Warn added in v0.7.0

func (n NameSpaceManager) Warn(msg string)

func (NameSpaceManager) Warnf added in v0.7.0

func (n NameSpaceManager) Warnf(format string, a ...interface{})

type Namespace

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

Namespace provides unique namespaces for keyval trees

func NewNamespace

func NewNamespace(name string) (ns *Namespace)

NewNamespace creates a new Namespace Instance. If this is intended to be persisted it should be registered to a NamespaceManager via RegisterNameSpace

func (*Namespace) FetchAbsolutePath

func (ns *Namespace) FetchAbsolutePath(path PathString) *PathElement

FetchAbsolutePath will return the PathElement instance at the end of the provided Path assuming it exists, otherwise it returns Nil

func (*Namespace) FetchAllAbsolutePaths

func (ns *Namespace) FetchAllAbsolutePaths() (allpaths []AbsolutePath, err error)

FetchAllAbsolutePaths returns an array of all distinct terminayted absolute paths effectively dumping all possible paths in the entire namespace

func (*Namespace) FetchOrCreateAbsolutePath added in v0.8.1

func (ns *Namespace) FetchOrCreateAbsolutePath(path PathString) (elem *PathElement, err error)

func (*Namespace) FindPathTail

func (ns *Namespace) FindPathTail(path PathString) *PathElement

FindPathTail attempts to locate the last element that most closely matches the given path fragment if no suitable match can be found, it returns Nil, if multiple elements are found, it returns the first one going from alphabetically-sorted pathing

func (*Namespace) RegisterAbsolutePath

func (ns *Namespace) RegisterAbsolutePath(path AbsolutePath) error

RegisterAbsolutePath constructs a complete path in the Namespace, with all required structure instances to make the path immediately available and active

type NamespaceManagerOpt

type NamespaceManagerOpt interface {
	// contains filtered or unexported methods
}

type PathElement

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

PathElement is an individual section of a complete path

func (*PathElement) AbsolutePath

func (p *PathElement) AbsolutePath() (path AbsolutePath)

AbsolutePath returns the full Path of this Element, as a single AbsolutePath instance

func (*PathElement) Add

func (p *PathElement) Add(path SubPath) (elem *PathElement, err error)

Add a Single subpath Element to this Element

func (*PathElement) AppendRelativePath

func (p *PathElement) AppendRelativePath(subPath PathString) (*PathElement, error)

AppendRelativePath constructs an element-relative subpath, append it to an Existing PathElement, creating all PathElements along the way

func (*PathElement) Chain

func (p *PathElement) Chain() (chain []*PathElement)

Chain returns the full Path of this Element, as a slice of individual PathElements

func (*PathElement) ContextLockPrefixWithLease

func (p *PathElement) ContextLockPrefixWithLease(octx context.Context, ttl time.Duration) (ctx *LeaseContext, release func())

ContextLockPrefixWithLease will lock a path element and all sub-elements with a timed lease on the lock you provide the context instance to have external control to cancel it before timeout

func (*PathElement) ContextLockWithLease

func (p *PathElement) ContextLockWithLease(octx context.Context, ttl time.Duration) (ctx *LeaseContext, release func())

ContextLockWithLease will lock a single path element with a timed lease on the lock you provide the context instance to have external control to cancel it before timeout

func (*PathElement) CreateSemaphorePool added in v0.8.0

func (p *PathElement) CreateSemaphorePool(prefix bool, purge bool, opts SemaphorePoolOpts) (err error)

CreateSemaphorePool instantiates a semaphore pool on this path element. prefix will attach the pool to all child elements purge will remove any existing semaphore pool, including from all children if prefix is true

func (PathElement) Debug added in v0.6.0

func (n PathElement) Debug(msg string)

func (PathElement) Debugf added in v0.6.0

func (n PathElement) Debugf(format string, a ...interface{})

func (*PathElement) Delete added in v0.8.0

func (p *PathElement) Delete() (err error)

func (*PathElement) EnablePruningAfter added in v0.8.0

func (p *PathElement) EnablePruningAfter(age time.Duration)

func (PathElement) Error added in v0.6.0

func (n PathElement) Error(msg string)

func (PathElement) Errorf added in v0.6.0

func (n PathElement) Errorf(format string, a ...interface{})

func (*PathElement) FetchAllSubPaths

func (p *PathElement) FetchAllSubPaths() (allpaths [][]SubPath, err error)

FetchAllSubPaths returns the SubPath location of all descendent PathElements underneath this PathElement

func (*PathElement) FetchClosestSubPath

func (p *PathElement) FetchClosestSubPath(subPath PathString) (pathchain []*PathElement)

FetchClosestSubPath will attempt to find the final Path Element that has the leading subpath string - this is relative to the pathelement itself, and is not an absolute path.

func (PathElement) FetchClosestSubPathTail

func (p PathElement) FetchClosestSubPathTail(subPath PathString) *PathElement

FetchClosestSubPathTail finds the last element in a path chain that most closely resembles the requested path

func (*PathElement) FetchSubPath

func (p *PathElement) FetchSubPath(subPath PathString) (*PathElement, error)

func (*PathElement) GetValue

func (p *PathElement) GetValue() (value ElementValue)

func (PathElement) Info added in v0.6.0

func (n PathElement) Info(msg string)

func (PathElement) Infof added in v0.6.0

func (n PathElement) Infof(format string, a ...interface{})

func (*PathElement) Lock

func (p *PathElement) Lock()

Lock places a Mutex on this pathElement and sends a notification of this lock to its chain of parent elements this also fulfills the interface Sync.Locker

func (*PathElement) LockPrefixWithLease

func (p *PathElement) LockPrefixWithLease(ttl time.Duration) (ctx *LeaseContext, release func())

LockPrefixWithLease will lock a path element and all sub-elements with a timed lease on the lock it uses a a background context so cannot be cancelled before the lease expires

func (*PathElement) LockSubs

func (p *PathElement) LockSubs()

LockSubs will lock this Path Element and every Path Element it is a parent to

func (*PathElement) LockWithLease

func (p *PathElement) LockWithLease(ttl time.Duration) (ctx *LeaseContext, release func())

LockWithLease will lock a single path element with a timed lease on the lock it uses a a background context so cannot be cancelled before the lease expires

func (*PathElement) Parent

func (p *PathElement) Parent() *PathElement

Parent returns the parent PathElement of this PathElement

func (*PathElement) ParentChain

func (p *PathElement) ParentChain() (parents []*PathElement)

ParentChain returns a slice of this Path Elements parent Pathelements, in order of parentage i.e, the first item is this elements immediate parent the last item is always the top-level (leftmost) pathelement

func (*PathElement) PreventPruning added in v0.8.0

func (p *PathElement) PreventPruning()

func (*PathElement) SetValue

func (p *PathElement) SetValue(value ElementValue, change changeType, actor access.Role)

func (*PathElement) SubPath

func (p *PathElement) SubPath() (path SubPath)

SubPath returns the name of this Path Element without the parent section of the path eg the 'file' portion of the path

func (*PathElement) SubscribeToEvents

func (p *PathElement) SubscribeToEvents(prefix bool) *ElementWatchSubscription

SubscribeToEvents generates a Watch Subscription that produces a single channel of notification events on the accompanying Path Element, and optionally all of its child path elements

func (*PathElement) UnLock

func (p *PathElement) UnLock()

UnLock will release the Mutex Lock on this path element Note that it will NOT unlock mutexes on sub-element unlocking will sent a notification event to the chain of parent elements this also fulfills the interface Sync.Locker

func (*PathElement) UnLockSubs

func (p *PathElement) UnLockSubs()

func (*PathElement) UnSubscribeFromEvents

func (p *PathElement) UnSubscribeFromEvents(sub *ElementWatchSubscription)

UnSubscribeFromEvents will unregister the notification channel and then nil out the watch subscription that is passed to it. preventing any further reception of events

func (PathElement) Warn added in v0.6.0

func (n PathElement) Warn(msg string)

func (PathElement) Warnf added in v0.6.0

func (n PathElement) Warnf(format string, a ...interface{})

type PathError

type PathError struct {
}

PathError indicates that processing a provided path was not possible either it a not a valid path identifier, or not a path that can be resolved.

type PathString

type PathString string

PathString is a string representation of all or part of a hierarchical set of resources in a namespace

func (PathString) ToAbsolutePath

func (m PathString) ToAbsolutePath() AbsolutePath

ToAbsolutePath converts a PathString into an AbsolutePath (a slice of ordered SubPath sections)

func (PathString) ToRelativePath

func (m PathString) ToRelativePath() RelativePath

ToRelativePath breaks down a path string into a relative Path

type RelativePath

type RelativePath []SubPath

RelativePath is the path to a single PathElement relative to a single PathElement somewhere in its parent chain

type SemaphoreClaim added in v0.8.0

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

func (*SemaphoreClaim) Return added in v0.8.0

func (p *SemaphoreClaim) Return() error

Return releases the semaphore claim back to the pool

type SemaphorePool added in v0.8.0

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

SemaphorePool is a combined semaphore for use by a PathElement and all its sub Elements

func (*SemaphorePool) Claim added in v0.8.1

func (p *SemaphorePool) Claim(ctx context.Context, slots int64) (claim *SemaphoreClaim, err error)

with use the provided context for timeout/cancellation

type SemaphorePoolOpts added in v0.8.0

type SemaphorePoolOpts struct {
	PoolSize int64 // Total Pool Weight available to divide amongst claims in this pool
	Prefix   bool
}

type SubPath

type SubPath string

SubPath is a path element identifier akin to the directory names between path delimeters

func (SubPath) Validate

func (m SubPath) Validate() error

Validate confirms that this SubPath entry is usable to construct a valid location within an AbsolutePath for a given Path Element

type WatchEvent

type WatchEvent struct {
	TS     time.Time
	Change changeType
	Actor  access.Role
	Note   string
	// contains filtered or unexported fields
}

WatchEvent describes an event on a Path Element or optionally any of its children, obtained and consumed via an ElementWatchSubscription

func (WatchEvent) OnElement

func (e WatchEvent) OnElement() *PathElement

type WatchEvents

type WatchEvents chan WatchEvent

type WithLogger added in v0.7.0

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

WithLogger attaches a Logger to the Namespace manager allowing you to insert your own logging solution into Whatnot

Directories

Path Synopsis
Package access provides Identity Tracking and optional Permissions management for managing and restricting namespace resources
Package access provides Identity Tracking and optional Permissions management for managing and restricting namespace resources

Jump to

Keyboard shortcuts

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