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

  1. package pubsub
  2. import "github.com/tendermint/tendermint/abci/types"
  3. // An item to be published to subscribers.
  4. type item struct {
  5. Data interface{}
  6. Events []types.Event
  7. }
  8. // A subInfo value records a single subscription.
  9. type subInfo struct {
  10. clientID string // chosen by the client
  11. query Query // chosen by the client
  12. subID string // assigned at registration
  13. sub *Subscription // receives published events
  14. }
  15. // A subInfoSet is an unordered set of subscription info records.
  16. type subInfoSet map[*subInfo]struct{}
  17. func (s subInfoSet) contains(si *subInfo) bool { _, ok := s[si]; return ok }
  18. func (s subInfoSet) add(si *subInfo) { s[si] = struct{}{} }
  19. func (s subInfoSet) remove(si *subInfo) { delete(s, si) }
  20. // withQuery returns the subset of s whose query string matches qs.
  21. func (s subInfoSet) withQuery(qs string) subInfoSet {
  22. out := make(subInfoSet)
  23. for si := range s {
  24. if si.query.String() == qs {
  25. out.add(si)
  26. }
  27. }
  28. return out
  29. }
  30. // A subIndex is an indexed collection of subscription info records.
  31. // The index is not safe for concurrent use without external synchronization.
  32. type subIndex struct {
  33. all subInfoSet // all subscriptions
  34. byClient map[string]subInfoSet // per-client subscriptions
  35. byQuery map[string]subInfoSet // per-query subscriptions
  36. // TODO(creachadair): We allow indexing by query to support existing use by
  37. // the RPC service methods for event streaming. Fix up those methods not to
  38. // require this, and then remove indexing by query.
  39. }
  40. // newSubIndex constructs a new, empty subscription index.
  41. func newSubIndex() *subIndex {
  42. return &subIndex{
  43. all: make(subInfoSet),
  44. byClient: make(map[string]subInfoSet),
  45. byQuery: make(map[string]subInfoSet),
  46. }
  47. }
  48. // findClients returns the set of subscriptions for the given client ID, or nil.
  49. func (idx *subIndex) findClientID(id string) subInfoSet { return idx.byClient[id] }
  50. // findQuery returns the set of subscriptions on the given query string, or nil.
  51. func (idx *subIndex) findQuery(qs string) subInfoSet { return idx.byQuery[qs] }
  52. // contains reports whether idx contains any subscription matching the given
  53. // client ID and query pair.
  54. func (idx *subIndex) contains(clientID, query string) bool {
  55. csubs, qsubs := idx.byClient[clientID], idx.byQuery[query]
  56. if len(csubs) == 0 || len(qsubs) == 0 {
  57. return false
  58. }
  59. for si := range csubs {
  60. if qsubs.contains(si) {
  61. return true
  62. }
  63. }
  64. return false
  65. }
  66. // add adds si to the index, replacing any previous entry with the same terms.
  67. // It is the caller's responsibility to check for duplicates before adding.
  68. // See also the contains method.
  69. func (idx *subIndex) add(si *subInfo) {
  70. idx.all.add(si)
  71. if m := idx.byClient[si.clientID]; m == nil {
  72. idx.byClient[si.clientID] = subInfoSet{si: struct{}{}}
  73. } else {
  74. m.add(si)
  75. }
  76. qs := si.query.String()
  77. if m := idx.byQuery[qs]; m == nil {
  78. idx.byQuery[qs] = subInfoSet{si: struct{}{}}
  79. } else {
  80. m.add(si)
  81. }
  82. }
  83. // removeAll removes all the elements of s from the index.
  84. func (idx *subIndex) removeAll(s subInfoSet) {
  85. for si := range s {
  86. idx.all.remove(si)
  87. idx.byClient[si.clientID].remove(si)
  88. if len(idx.byClient[si.clientID]) == 0 {
  89. delete(idx.byClient, si.clientID)
  90. }
  91. if si.query != nil {
  92. qs := si.query.String()
  93. idx.byQuery[qs].remove(si)
  94. if len(idx.byQuery[qs]) == 0 {
  95. delete(idx.byQuery, qs)
  96. }
  97. }
  98. }
  99. }