fixed error handling and docs
This commit is contained in:
23
README.md
23
README.md
@@ -11,7 +11,7 @@ go get -u gitlab.com/naterciom/gubgub
|
|||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Ignoring errors for code brevity!
|
We'll be ignoring errors for code brevity!
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
package main
|
package main
|
||||||
@@ -32,22 +32,33 @@ func consumer(msg MyMessage) {
|
|||||||
fmt.Printf("Hello %s", msg.Name)
|
fmt.Printf("Hello %s", msg.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func m2ain() {
|
||||||
topic := gubgub.NewAsyncTopic[MyMessage]()
|
topic := gubgub.NewAsyncTopic[MyMessage]()
|
||||||
defer topic.Close() // Returns after all messages are delivered
|
defer topic.Close() // Returns after all messages are delivered
|
||||||
|
|
||||||
_ := topic.Subscribe(gubgub.Forever(consumer))
|
_ = topic.Subscribe(gubgub.Forever(consumer))
|
||||||
|
|
||||||
// The AsyncTopic doesn't wait for the subscriber to be registered so, for the purposes of this
|
// The AsyncTopic doesn't wait for the subscriber to be registered so, for the purposes of this
|
||||||
// example, we sleep on it.
|
// example, we sleep on it.
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
_ := topic.Publish(MyMessage{Name: "John Smith"}) // Returns immediately
|
_ = topic.Publish(MyMessage{Name: "John Smith"}) // Returns immediately
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
Topics are what this is all about. You publish to a topic and you subscribe to a topic. That is it.
|
Topics are what this is all about.
|
||||||
|
You publish to a topic and you subscribe to a topic.
|
||||||
|
That is it.
|
||||||
|
|
||||||
|
A `Subscriber` is just a callback func.
|
||||||
|
A message is considered delivered when all subscribers have been called for that message and returned.
|
||||||
|
|
||||||
|
If you `Publish` a message successfully (did not get an error) then you can be sure the message will be deliverd before any call to `Close` returns.
|
||||||
|
|
||||||
|
Topics are meant to live as long as the application but you should call the `Close` method upon shutdown to fulfill the publishing promise.
|
||||||
|
Use the `WithOnClose` option when creating the topic to perform any extra clean up you might need to do if the topic is closed.
|
||||||
|
|
||||||
GubGub offers 2 kinds of topics:
|
GubGub offers 2 kinds of topics:
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,28 @@ func main() {
|
|||||||
topic := gubgub.NewAsyncTopic[string]()
|
topic := gubgub.NewAsyncTopic[string]()
|
||||||
defer topic.Close()
|
defer topic.Close()
|
||||||
|
|
||||||
topic.Subscribe(gubgub.Forever(UpperCaser))
|
err := topic.Subscribe(gubgub.Forever(UpperCaser))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
topic.Subscribe(Countdown(3))
|
err = topic.Subscribe(Countdown(3))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
topic.Subscribe(gubgub.Buffered(gubgub.Forever(Slow)))
|
err = topic.Subscribe(gubgub.Buffered(gubgub.Forever(Slow)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for s := range gubgub.Feed(topic, false) {
|
feed, err := gubgub.Feed(topic, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for s := range feed {
|
||||||
log.Printf("ForRange: %s", s)
|
log.Printf("ForRange: %s", s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -35,6 +49,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
topic.Publish(scanner.Text())
|
topic.Publish(scanner.Text())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
feed.go
15
feed.go
@@ -1,10 +1,12 @@
|
|||||||
package gubgub
|
package gubgub
|
||||||
|
|
||||||
import "iter"
|
import (
|
||||||
|
"iter"
|
||||||
|
)
|
||||||
|
|
||||||
// Feed allows the usage of for/range to consume future published messages.
|
// Feed allows the usage of for/range loop to consume future published messages.
|
||||||
// The supporting subscriber will eventually be discarded after you exit the for loop.
|
// The supporting subscriber will eventually be discarded after you exit the for loop.
|
||||||
func Feed[T any](t Subscribable[T], buffered bool) iter.Seq[T] {
|
func Feed[T any](t Subscribable[T], buffered bool) (iter.Seq[T], error) {
|
||||||
feed := make(chan T) // closed by the subscriber
|
feed := make(chan T) // closed by the subscriber
|
||||||
unsubscribe := make(chan struct{}) // closed by the iterator
|
unsubscribe := make(chan struct{}) // closed by the iterator
|
||||||
|
|
||||||
@@ -22,7 +24,10 @@ func Feed[T any](t Subscribable[T], buffered bool) iter.Seq[T] {
|
|||||||
subscriber = Buffered(subscriber)
|
subscriber = Buffered(subscriber)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Subscribe(subscriber)
|
err := t.Subscribe(subscriber)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Iterator
|
// Iterator
|
||||||
return func(yield func(T) bool) {
|
return func(yield func(T) bool) {
|
||||||
@@ -33,5 +38,5 @@ func Feed[T any](t Subscribable[T], buffered bool) iter.Seq[T] {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,12 @@ func TestFeed_Topics(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
feed, err := Feed(tc.topic, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
feedback := make(chan int)
|
feedback := make(chan int)
|
||||||
go func() {
|
go func() {
|
||||||
for i := range Feed(tc.topic, false) {
|
for i := range feed {
|
||||||
feedback <- i
|
feedback <- i
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -58,5 +61,4 @@ func TestFeed_Topics(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ func NoOp[T any]() Subscriber[T] {
|
|||||||
// available memory.
|
// available memory.
|
||||||
// This is useful if message publishing is surge prone and message processing is slow or
|
// This is useful if message publishing is surge prone and message processing is slow or
|
||||||
// unpredictable (for example: subscriber makes network request).
|
// unpredictable (for example: subscriber makes network request).
|
||||||
|
// IMPORTANT: messages are considered delivered even it they are still in the buffer which means
|
||||||
|
// that buffered subscribers are NOT COVERED by the publishing promise.
|
||||||
// Message average processing rate must still be higher than the average message publishing rate
|
// Message average processing rate must still be higher than the average message publishing rate
|
||||||
// otherwise it will eventually lead to memory issues. You will need to find a better strategy to
|
// otherwise it will eventually lead to memory issues. You will need to find a better strategy to
|
||||||
// deal with such scenario.
|
// deal with such scenario.
|
||||||
|
|||||||
Reference in New Issue
Block a user