resonate
resonate copied to clipboard
Add Announcements
Describe the problem you are facing
Resonate's Deterministic Simulation Testing cannot observe the Resonate Server beyond the request/response API. E.g. the Deterministic Simulation Testing cannot observe if notifications are actually sent.
Describe the solution you'd like
P-Lang style announcements & monitors. With announcements and monitors, DST will be able to verify that e.g. announcements are sent if a corresponding subscription exists.
Implementation Details
Step 1
Create a package announcements
.
Step 2
Define an interface Announcement
with a function
type Announcement interface {
Announce(data map[string]string)
}
Step 3
Define two different implementations:
-
NopAnnouncement
-
DstAnnouncement
Step 4
Use a package-level private variable to hold a singleton instance. Instantiate the correct singleton instance based on config parameter on startup e.g.
package announcements
import "sync"
var (
instance Announcement
once sync.Once
)
func Initialize(envType EnvironmentType) {
once.Do(func() {
switch envType {
case Nop:
instance = &NopAnnouncement{}
case Dst:
instance = &DstAnnouncement{}
default:
// Handle default case or throw an error
}
})
}
// Additional code to define EnvironmentType, NopAnnouncement, and DstAnnouncement follows...
Step 5
Implement the interface
-
Nop
This will simply implement the Announce method without performing any operations. -
Dst
This will need to safely add the announcement data to an array for later verification. Ensure thread safety
The network is a good candidate to add a first announcement
https://github.com/resonatehq/resonate/blob/7935e1cae3c90e04152d23f2170acdb101a73946/internal/kernel/t_aio/network.go#L22
Add a custom event struct. The only required parameter is type
, everything else is a key value map
type Event struct {
Type string
data map[string]interface{}
}
func NewEvent(type string, initialData ...map[string]interface{}) *Event {
var data map[string]interface{}
if len(initialData) > 0 {
data = initialData[0] // Use the first map provided if any.
} else {
data = make(map[string]interface{})
}
return &Event{
Type: eventType,
data: data
}
}
// Set adds or updates a key with a given value.
func (e *Event) Set(key string, value interface{}) {
e.data[key] = value
}
// Get tries to retrieve a value of the specified type T from the event data. It returns the zero value and an error if the type does not match.
func [T any](e *Event) Get[T any](key string) (T, error) {
value, exists := e.data[key]
if !exists {
var zero T
panic("key %s does not exist in event of type %s", key, e.Type)
}
typedValue, ok := value.(T)
if !ok {
panic("value at key %s is not of the requested type in event of type %s", key, e.Type)
}
return typedValue, nil
}
Regarding announcements: The announcement (the event) should contain all (relevant) application level information. For example, in the context of networking, the event should contain the url, http method, and http body