From 017600579713727d58c6bc79cc7f917d7a9f1959 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 31 Jan 2022 16:07:28 +0000 Subject: [PATCH] Clarify usage of WatchSet methods and behaviour of First/Last (#118) * Clarify behaviour of existing WatchSet methods * Clarify behaviour of First/Last(Watch) & LongestPrefix --- txn.go | 33 +++++++++++++++++++++++++++++---- watch.go | 6 +++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/txn.go b/txn.go index 1a9da1a..c3fe455 100644 --- a/txn.go +++ b/txn.go @@ -513,7 +513,15 @@ func (txn *Txn) DeleteAll(table, index string, args ...interface{}) (int, error) } // FirstWatch is used to return the first matching object for -// the given constraints on the index along with the watch channel +// the given constraints on the index along with the watch channel. +// +// Note that all values read in the transaction form a consistent snapshot +// from the time when the transaction was created. +// +// The watch channel is closed when a subsequent write transaction +// has updated the result of the query. Since each read transaction +// operates on an isolated snapshot, a new read transaction must be +// started to observe the changes that have been made. func (txn *Txn) FirstWatch(table, index string, args ...interface{}) (<-chan struct{}, interface{}, error) { // Get the index value indexSchema, val, err := txn.getIndexValue(table, index, args...) @@ -541,7 +549,15 @@ func (txn *Txn) FirstWatch(table, index string, args ...interface{}) (<-chan str } // LastWatch is used to return the last matching object for -// the given constraints on the index along with the watch channel +// the given constraints on the index along with the watch channel. +// +// Note that all values read in the transaction form a consistent snapshot +// from the time when the transaction was created. +// +// The watch channel is closed when a subsequent write transaction +// has updated the result of the query. Since each read transaction +// operates on an isolated snapshot, a new read transaction must be +// started to observe the changes that have been made. func (txn *Txn) LastWatch(table, index string, args ...interface{}) (<-chan struct{}, interface{}, error) { // Get the index value indexSchema, val, err := txn.getIndexValue(table, index, args...) @@ -569,14 +585,20 @@ func (txn *Txn) LastWatch(table, index string, args ...interface{}) (<-chan stru } // First is used to return the first matching object for -// the given constraints on the index +// the given constraints on the index. +// +// Note that all values read in the transaction form a consistent snapshot +// from the time when the transaction was created. func (txn *Txn) First(table, index string, args ...interface{}) (interface{}, error) { _, val, err := txn.FirstWatch(table, index, args...) return val, err } // Last is used to return the last matching object for -// the given constraints on the index +// the given constraints on the index. +// +// Note that all values read in the transaction form a consistent snapshot +// from the time when the transaction was created. func (txn *Txn) Last(table, index string, args ...interface{}) (interface{}, error) { _, val, err := txn.LastWatch(table, index, args...) return val, err @@ -589,6 +611,9 @@ func (txn *Txn) Last(table, index string, args ...interface{}) (interface{}, err // null and fail to find a leaf node). This should only be used where the prefix // given is capable of matching indexed entries directly, which typically only // applies to a custom indexer. See the unit test for an example. +// +// Note that all values read in the transaction form a consistent snapshot +// from the time when the transaction was created. func (txn *Txn) LongestPrefix(table, index string, args ...interface{}) (interface{}, error) { // Enforce that this only works on prefix indexes. if !strings.HasSuffix(index, "_prefix") { diff --git a/watch.go b/watch.go index 7de78a1..d9ab4fd 100644 --- a/watch.go +++ b/watch.go @@ -42,7 +42,7 @@ func (w WatchSet) AddWithLimit(softLimit int, watchCh <-chan struct{}, altCh <-c } } -// Watch is used to wait for either the watch set to trigger or a timeout. +// Watch is used to wait for any channel of the watch set to trigger or a timeout. // Returns true on timeout. func (w WatchSet) Watch(timeoutCh <-chan time.Time) bool { if w == nil { @@ -64,7 +64,7 @@ func (w WatchSet) Watch(timeoutCh <-chan time.Time) bool { return w.WatchCtx(ctx) == context.Canceled } -// WatchCtx is used to wait for either the watch set to trigger or for the +// WatchCtx is used to wait for any channel of the watch set to trigger or for the // context to be cancelled. Watch with a timeout channel can be mimicked by // creating a context with a deadline. WatchCtx should be preferred over Watch. func (w WatchSet) WatchCtx(ctx context.Context) error { @@ -128,7 +128,7 @@ func (w WatchSet) watchMany(ctx context.Context) error { } } -// WatchCh returns a channel that is used to wait for either the watch set to trigger +// WatchCh returns a channel that is used to wait for any channel of the watch set to trigger // or for the context to be cancelled. WatchCh creates a new goroutine each call, so // callers may need to cache the returned channel to avoid creating extra goroutines. func (w WatchSet) WatchCh(ctx context.Context) <-chan error {