-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Announcements and Monitoring of DST #269
Changes from all commits
435b1a8
a19ed66
b2ed5f8
f355611
ead66fc
153dac9
4b69efd
6717d19
e386f65
22837d8
e03f15f
25e331a
e597bea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package announcements | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
) | ||
|
||
type Event struct { | ||
Type string | ||
Data map[string]interface{} | ||
} | ||
|
||
func NewEvent(eventType 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, | ||
} | ||
} | ||
|
||
func (e *Event) Set(key string, value interface{}) { | ||
e.Data[key] = value | ||
} | ||
|
||
func (e *Event) Get(key string) (interface{}, error) { | ||
value, exists := e.Data[key] | ||
if !exists { | ||
return nil, fmt.Errorf("key '%s' does not exist in event of type '%s'", key, e.Type) | ||
} | ||
return value, nil | ||
} | ||
|
||
type Announcement interface { | ||
Announce(event *Event) | ||
Register(monitor Monitors) | ||
} | ||
|
||
type NoopAnnouncement struct{} | ||
|
||
type DstAnnouncement struct { | ||
announcements []Event | ||
monitors []Monitors | ||
mutex sync.Mutex // Mutex for thread safety | ||
} | ||
|
||
// Register implements Announcement. | ||
func (d *NoopAnnouncement) Register(monitor Monitors) { | ||
// Do nothing | ||
} | ||
|
||
func (d *DstAnnouncement) Register(monitor Monitors) { | ||
d.mutex.Lock() | ||
defer d.mutex.Unlock() | ||
d.monitors = append(d.monitors, monitor) | ||
} | ||
|
||
var ( | ||
instance Announcement | ||
once sync.Once | ||
) | ||
|
||
type EnvironmentType int | ||
|
||
const ( | ||
Noop EnvironmentType = iota | ||
Dst | ||
) | ||
|
||
func Initialize(envType EnvironmentType, monitors []Monitors) { | ||
once.Do(func() { | ||
switch envType { | ||
case Noop: | ||
instance = &NoopAnnouncement{} | ||
case Dst: | ||
instance = &DstAnnouncement{ | ||
announcements: make([]Event, 0, 100), // Preallocate capacity to prevent frequent reallocations | ||
monitors: monitors, | ||
} | ||
default: | ||
panic("Invalid environment type.") | ||
} | ||
}) | ||
} | ||
|
||
func GetInstance() Announcement { | ||
// check if the instance has been initialized | ||
return instance | ||
} | ||
|
||
func (n *NoopAnnouncement) Announce(event *Event) { | ||
// Do nothing | ||
} | ||
|
||
func (d *DstAnnouncement) Announce(event *Event) { | ||
d.mutex.Lock() | ||
defer d.mutex.Unlock() | ||
d.announcements = append(d.announcements, *event) | ||
// Print the announcement | ||
fmt.Println("Announcement:", event.Type, event.Data) | ||
// Apply the all the registered monitors | ||
for _, monitor := range d.monitors { | ||
monitor.Apply(*event) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// monitor.go | ||
|
||
package announcements | ||
|
||
type Monitors interface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rename the interface to |
||
Apply(event Event) | ||
Status() []error | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package monitors | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/resonatehq/resonate/internal/announcements" | ||
) | ||
|
||
type taskMonitor struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TaskMonitor must not part of the core package for announcements and monitors, but part of the test/dst -- it's application specific |
||
eventTypeCount map[string]int // Map to count occurrences of each event type | ||
mutex sync.Mutex // Mutex for thread safety | ||
} | ||
|
||
func NewTaskMonitor() *taskMonitor { | ||
return &taskMonitor{ | ||
eventTypeCount: make(map[string]int), | ||
} | ||
} | ||
|
||
func (tm *taskMonitor) Apply(event announcements.Event) { | ||
tm.mutex.Lock() | ||
defer tm.mutex.Unlock() | ||
|
||
// Increment event type count | ||
tm.eventTypeCount[event.Type]++ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the right idea: When an event happens, modify a counter. When we check the monitors at the end of the run, here we want taskCreated == taskCompleted. If that check is too aggressive, at least we want to check that taskCreated >= taskCompleted
|
||
|
||
// If the event type is TaskCreated or TaskCompleted, print the event | ||
if event.Type == "TaskCreated" || event.Type == "TaskCompleted" { | ||
fmt.Println("Received event:", event.Type, event.Data) | ||
} | ||
} | ||
|
||
// Status returns any errors or status information associated with the taskMonitor. | ||
func (tm *taskMonitor) Status() []error { | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"net/http" | ||
|
||
"github.com/resonatehq/resonate/internal/aio" | ||
"github.com/resonatehq/resonate/internal/announcements" | ||
"github.com/resonatehq/resonate/internal/kernel/bus" | ||
"github.com/resonatehq/resonate/internal/kernel/t_aio" | ||
"github.com/resonatehq/resonate/internal/util" | ||
|
@@ -69,10 +70,22 @@ func (d *NetworkDSTDevice) Process(sqes []*bus.SQE[t_aio.Submission, t_aio.Compl | |
res = &http.Response{ | ||
StatusCode: http.StatusOK, | ||
} | ||
|
||
event := announcements.NewEvent("HTTPResponse") | ||
vaibhawvipul marked this conversation as resolved.
Show resolved
Hide resolved
|
||
event.Set("StatusCode", res.StatusCode) | ||
event.Set("URL", sqe.Submission.Network.Http.Url) | ||
event.Set("Method", sqe.Submission.Network.Http.Method) | ||
announcements.GetInstance().Announce(event) | ||
} else { | ||
res = &http.Response{ | ||
StatusCode: http.StatusInternalServerError, | ||
} | ||
|
||
event := announcements.NewEvent("HTTPResponse") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good!! |
||
event.Set("StatusCode", res.StatusCode) | ||
event.Set("URL", sqe.Submission.Network.Http.Url) | ||
event.Set("Method", sqe.Submission.Network.Http.Method) | ||
announcements.GetInstance().Announce(event) | ||
} | ||
|
||
cqe.Completion = &t_aio.Completion{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually a better practice to have the mutex at the start of the struct.
As the struct need not calculate the offset every time to find the mutex variable inside the struct.