pubsub

package module
v0.0.0-...-60af269 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2023 License: MIT Imports: 3 Imported by: 0

README

Pubsub

GoDoc

This package is a simple publisher-subscriber library in Golang. It takes advantage of the generics added in Go1.18 to create a pubsub system keyed by types. This removes some of the type assertions that typically come with systems that rely on any.

Non-generic pubsub systems

Let's say we are trying to pass around a struct UserEvent. A typical pubsub system will look something like this:

Publishing code:

// Payload is usually type any
event := pubsub.Event{
    Payload: UserEvent{}
}

// UserEventTopic is some arbitrary constant to control what subscribers
// receive this event.
pubsub.Publish(UserEventTopic, event)

Subscribing code:

// Setting up the subscriber
// Use the same constant for the subscription code
eventCh, unsub := pubsub.Subscribe(UserEventTopic)
defer unsub()

// Handling events
event := <- eventCh

// Type cast back to the type we need
userEvent, ok := event.Payload.(UserEvent)

if !ok {
    // Handle incompatible type
}

This system lacks type safety, there's nothing that prevents calling code from passing other data types on the topic UserEventTopic. The system needs to maintain a list of arbitrary topics and their associated types.

Examples

With this library, the topics are the types themselves, so no external topic list needs to be maintained and the type assertions are built into the pubsub system. Lets see the same example using this package:

Publishing code:

userEvent := UserEvent{}

// No need to wrap the data in a new type or specify an arbitrary constant.
pubsub.Publish(ctx, userEvent)

Subscribing code:

// Setting up the subscriber
userEventCh, unsub := pubsub.SubscribeTo[UserEvent](ctx)
defer unsub()

// Handling events
// Values come out of the channel with the correct type
userEvent := <- userEventCh
Working with interface types

Publishing to interface types is more complicated since they have an underlying type.

// Subscribe to an interface type
errCh, unsub := pubsub.SubscribeTo[error](ctx)
// Some struct that implements the error interface
err := userError{}

// BAD, this will publish to the userError topic
pubsub.Publish(ctx, err)

// GOOD, this publishes to the error topic
pubsub.Publish[error](ctx, err)

Limitations

Keying topics by type has some limitations introduced by the golang type system. Specifically, non-hashable types cannot be subscribed to. Here is a comprehensive list of golang types and whether or not they work with this pubsub system.

As of golang 1.21.3:

Type Compatibility Workaround
Primitives Yes
Pointers Yes
Structs Yes
Slices No Wrap the slice in a struct type
Maps No Wrap the map in a struct type
Channels Yes
Functions No Use an interface type instead
Interfaces Yes* See interface example

Documentation

Overview

Package pubsub provides a simple publisher/subscriber system that is type-safe and generic. Events are published and subscribed to according to type. Due to the nature of golangs type system, some types (slices, maps, and funcs) do not work with this system.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Publish

func Publish[T any](ctx context.Context, val T)

Publish will send the value val into the global event scope. If the context is canceled, the value may not be sent to all subscribers.

func PublishToScope

func PublishToScope[T any](ctx context.Context, e *EventScope, val T)

PublishToScope will send the value val on the specified event scope. If the context is canceled, the value may not be sent to all subscribers.

Types

type EventScope

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

EventScope describes a scope where publishers and subscribers communicate with each other. Data published to an event scope will not be seen by subscribers listening on a differnt event scope. Multiple event scopes should only be used when you need to publish data with the same type but different handlers.

var (
	// Global is the default event scope. Publish and SubscribeTo use this event scope.
	Global *EventScope
)

func NewEventScope

func NewEventScope() *EventScope

type UnsubFn

type UnsubFn func()

UnSubFn is a function which unsubscribes from the data type. Calling this will close the channel returned by SubscribeTo/SubscribeToScope.

func SubscribeTo

func SubscribeTo[T any](ctx context.Context) (chan T, UnsubFn)

SubscribeTo creates a channel to listen for events of type T. When listeners are finished processing these events, the UnsubFn should be called.

func SubscribeToScope

func SubscribeToScope[T any](ctx context.Context, e *EventScope) (chan T, UnsubFn)

SubscribeTo creates a channel to listen for events of type T published on the provided event scope. When listeners are finished processing these events, the UnsubFn should be called.

Jump to

Keyboard shortcuts

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