You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

113 lines
3.3 KiB

package pubsub
import "github.com/tendermint/tendermint/abci/types"
// An item to be published to subscribers.
type item struct {
Data interface{}
Events []types.Event
}
// A subInfo value records a single subscription.
type subInfo struct {
clientID string // chosen by the client
query Query // chosen by the client
subID string // assigned at registration
sub *Subscription // receives published events
}
// A subInfoSet is an unordered set of subscription info records.
type subInfoSet map[*subInfo]struct{}
func (s subInfoSet) contains(si *subInfo) bool { _, ok := s[si]; return ok }
func (s subInfoSet) add(si *subInfo) { s[si] = struct{}{} }
func (s subInfoSet) remove(si *subInfo) { delete(s, si) }
// withQuery returns the subset of s whose query string matches qs.
func (s subInfoSet) withQuery(qs string) subInfoSet {
out := make(subInfoSet)
for si := range s {
if si.query.String() == qs {
out.add(si)
}
}
return out
}
// A subIndex is an indexed collection of subscription info records.
// The index is not safe for concurrent use without external synchronization.
type subIndex struct {
all subInfoSet // all subscriptions
byClient map[string]subInfoSet // per-client subscriptions
byQuery map[string]subInfoSet // per-query subscriptions
// TODO(creachadair): We allow indexing by query to support existing use by
// the RPC service methods for event streaming. Fix up those methods not to
// require this, and then remove indexing by query.
}
// newSubIndex constructs a new, empty subscription index.
func newSubIndex() *subIndex {
return &subIndex{
all: make(subInfoSet),
byClient: make(map[string]subInfoSet),
byQuery: make(map[string]subInfoSet),
}
}
// findClients returns the set of subscriptions for the given client ID, or nil.
func (idx *subIndex) findClientID(id string) subInfoSet { return idx.byClient[id] }
// findQuery returns the set of subscriptions on the given query string, or nil.
func (idx *subIndex) findQuery(qs string) subInfoSet { return idx.byQuery[qs] }
// contains reports whether idx contains any subscription matching the given
// client ID and query pair.
func (idx *subIndex) contains(clientID, query string) bool {
csubs, qsubs := idx.byClient[clientID], idx.byQuery[query]
if len(csubs) == 0 || len(qsubs) == 0 {
return false
}
for si := range csubs {
if qsubs.contains(si) {
return true
}
}
return false
}
// add adds si to the index, replacing any previous entry with the same terms.
// It is the caller's responsibility to check for duplicates before adding.
// See also the contains method.
func (idx *subIndex) add(si *subInfo) {
idx.all.add(si)
if m := idx.byClient[si.clientID]; m == nil {
idx.byClient[si.clientID] = subInfoSet{si: struct{}{}}
} else {
m.add(si)
}
qs := si.query.String()
if m := idx.byQuery[qs]; m == nil {
idx.byQuery[qs] = subInfoSet{si: struct{}{}}
} else {
m.add(si)
}
}
// removeAll removes all the elements of s from the index.
func (idx *subIndex) removeAll(s subInfoSet) {
for si := range s {
idx.all.remove(si)
idx.byClient[si.clientID].remove(si)
if len(idx.byClient[si.clientID]) == 0 {
delete(idx.byClient, si.clientID)
}
if si.query != nil {
qs := si.query.String()
idx.byQuery[qs].remove(si)
if len(idx.byQuery[qs]) == 0 {
delete(idx.byQuery, qs)
}
}
}
}