@@ -32,6 +32,7 @@ import (
32
32
"sync"
33
33
"time"
34
34
35
+ "github.com/tochemey/goakt/v2/address"
35
36
"go.uber.org/atomic"
36
37
"google.golang.org/protobuf/proto"
37
38
@@ -71,7 +72,7 @@ type Engine struct {
71
72
remotingPort int
72
73
minimumPeersQuorum uint16
73
74
eventStream eventstream.Stream
74
- locker * sync.Mutex
75
+ mutex * sync.Mutex
75
76
}
76
77
77
78
// NewEngine creates an instance of Engine
@@ -82,7 +83,7 @@ func NewEngine(name string, eventsStore eventstore.EventsStore, opts ...Option)
82
83
enableCluster : atomic .NewBool (false ),
83
84
logger : log .New (log .ErrorLevel , os .Stderr ),
84
85
eventStream : eventstream .New (),
85
- locker : & sync.Mutex {},
86
+ mutex : & sync.Mutex {},
86
87
}
87
88
88
89
for _ , opt := range opts {
@@ -94,133 +95,152 @@ func NewEngine(name string, eventsStore eventstore.EventsStore, opts ...Option)
94
95
}
95
96
96
97
// Start starts the ego engine
97
- func (x * Engine ) Start (ctx context.Context ) error {
98
+ func (engine * Engine ) Start (ctx context.Context ) error {
98
99
opts := []actors.Option {
99
- actors .WithLogger (x .logger ),
100
+ actors .WithLogger (engine .logger ),
100
101
actors .WithPassivationDisabled (),
101
102
actors .WithActorInitMaxRetries (1 ),
102
103
actors .WithReplyTimeout (5 * time .Second ),
103
104
actors .WithSupervisorDirective (actors .NewStopDirective ()),
104
105
}
105
106
106
- if x .enableCluster .Load () {
107
- if x .hostName == "" {
108
- x .hostName , _ = os .Hostname ()
107
+ if engine .enableCluster .Load () {
108
+ if engine .hostName == "" {
109
+ engine .hostName , _ = os .Hostname ()
109
110
}
110
111
111
112
replicaCount := 1
112
- if x .minimumPeersQuorum > 1 {
113
+ if engine .minimumPeersQuorum > 1 {
113
114
replicaCount = 2
114
115
}
115
116
116
117
clusterConfig := actors .
117
118
NewClusterConfig ().
118
- WithDiscovery (x .discoveryProvider ).
119
- WithDiscoveryPort (x .gossipPort ).
120
- WithPeersPort (x .peersPort ).
121
- WithMinimumPeersQuorum (uint32 (x .minimumPeersQuorum )).
119
+ WithDiscovery (engine .discoveryProvider ).
120
+ WithDiscoveryPort (engine .gossipPort ).
121
+ WithPeersPort (engine .peersPort ).
122
+ WithMinimumPeersQuorum (uint32 (engine .minimumPeersQuorum )).
122
123
WithReplicaCount (uint32 (replicaCount )).
123
- WithPartitionCount (x .partitionsCount ).
124
+ WithPartitionCount (engine .partitionsCount ).
124
125
WithKinds (new (actor ))
125
126
126
127
opts = append (opts ,
127
128
actors .WithCluster (clusterConfig ),
128
- actors .WithRemoting (x .hostName , int32 (x .remotingPort )))
129
+ actors .WithRemoting (engine .hostName , int32 (engine .remotingPort )))
129
130
}
130
131
131
132
var err error
132
- x .actorSystem , err = actors .NewActorSystem (x .name , opts ... )
133
+ engine .actorSystem , err = actors .NewActorSystem (engine .name , opts ... )
133
134
if err != nil {
134
- x .logger .Error (fmt .Errorf ("failed to create the ego actor system: %w" , err ))
135
- return err
135
+ return fmt .Errorf ("failed to create the ego actor system: %w" , err )
136
136
}
137
137
138
- if err := x .actorSystem .Start (ctx ); err != nil {
138
+ if err := engine .actorSystem .Start (ctx ); err != nil {
139
139
return err
140
140
}
141
141
142
- x .started .Store (true )
142
+ engine .started .Store (true )
143
143
144
144
return nil
145
145
}
146
146
147
- // AddProjection add a projection to the running eGo engine and start it
148
- func (x * Engine ) AddProjection (ctx context.Context , name string , handler projection.Handler , offsetStore offsetstore.OffsetStore , opts ... projection.Option ) error {
149
- x .locker .Lock ()
150
- started := x .started .Load ()
151
- x .locker .Unlock ()
152
- if ! started {
147
+ // AddProjection add a projection to the running eGo engine and starts it
148
+ func (engine * Engine ) AddProjection (ctx context.Context , name string , handler projection.Handler , offsetStore offsetstore.OffsetStore , opts ... projection.Option ) error {
149
+ if ! engine .Started () {
153
150
return ErrEngineNotStarted
154
151
}
155
152
156
- actor := projection .New (name , handler , x .eventsStore , offsetStore , opts ... )
153
+ actor := projection .New (name , handler , engine .eventsStore , offsetStore , opts ... )
157
154
158
- var (
159
- pid * actors.PID
160
- err error
161
- )
155
+ engine .mutex .Lock ()
156
+ actorSystem := engine .actorSystem
157
+ engine .mutex .Unlock ()
162
158
163
- x . locker . Lock ()
164
- actorSystem := x . actorSystem
165
- x . locker . Unlock ()
159
+ if _ , err := actorSystem . Spawn ( ctx , name , actor ); err != nil {
160
+ return fmt . Errorf ( "failed to register the projection=(%s): %w" , name , err )
161
+ }
166
162
167
- if pid , err = actorSystem .Spawn (ctx , name , actor ); err != nil {
168
- x .logger .Error (fmt .Errorf ("failed to register the projection=(%s): %w" , name , err ))
169
- return err
163
+ return nil
164
+ }
165
+
166
+ // RemoveProjection stops and removes a given projection from the engine
167
+ func (engine * Engine ) RemoveProjection (ctx context.Context , name string ) error {
168
+ if ! engine .Started () {
169
+ return ErrEngineNotStarted
170
170
}
171
171
172
- if err := actors .Tell (ctx , pid , projection .Start ); err != nil {
173
- x .logger .Error (fmt .Errorf ("failed to start the projection=(%s): %w" , name , err ))
174
- return err
172
+ engine .mutex .Lock ()
173
+ actorSystem := engine .actorSystem
174
+ engine .mutex .Unlock ()
175
+
176
+ return actorSystem .Kill (ctx , name )
177
+ }
178
+
179
+ // IsProjectionRunning returns true when the projection is active and running
180
+ // One needs to check the error to see whether this function does not return a false negative
181
+ func (engine * Engine ) IsProjectionRunning (ctx context.Context , name string ) (bool , error ) {
182
+ if ! engine .Started () {
183
+ return false , ErrEngineNotStarted
175
184
}
185
+ engine .mutex .Lock ()
186
+ actorSystem := engine .actorSystem
187
+ engine .mutex .Unlock ()
176
188
177
- return nil
189
+ addr , pid , err := actorSystem .ActorOf (ctx , name )
190
+ if err != nil {
191
+ return false , fmt .Errorf ("failed to get projection %s: %w" , name , err )
192
+ }
193
+
194
+ if pid != nil {
195
+ return pid .IsRunning (), nil
196
+ }
197
+
198
+ return addr != nil && proto .Equal (addr .Address , address .NoSender ), nil
178
199
}
179
200
180
201
// Stop stops the ego engine
181
- func (x * Engine ) Stop (ctx context.Context ) error {
182
- x .started .Store (false )
183
- x .eventStream .Close ()
184
- return x .actorSystem .Stop (ctx )
202
+ func (engine * Engine ) Stop (ctx context.Context ) error {
203
+ engine .started .Store (false )
204
+ engine .eventStream .Close ()
205
+ return engine .actorSystem .Stop (ctx )
206
+ }
207
+
208
+ // Started returns true when the eGo engine has started
209
+ func (engine * Engine ) Started () bool {
210
+ return engine .started .Load ()
185
211
}
186
212
187
213
// Subscribe creates an events subscriber
188
- func (x * Engine ) Subscribe () (eventstream.Subscriber , error ) {
189
- x .locker .Lock ()
190
- started := x .started .Load ()
191
- x .locker .Unlock ()
192
- if ! started {
214
+ func (engine * Engine ) Subscribe () (eventstream.Subscriber , error ) {
215
+ if ! engine .Started () {
193
216
return nil , ErrEngineNotStarted
194
217
}
195
218
196
- x . locker .Lock ()
197
- eventStream := x .eventStream
198
- x . locker .Unlock ()
219
+ engine . mutex .Lock ()
220
+ eventStream := engine .eventStream
221
+ engine . mutex .Unlock ()
199
222
200
223
subscriber := eventStream .AddSubscriber ()
201
- for i := 0 ; i < int (x .partitionsCount ); i ++ {
224
+ for i := 0 ; i < int (engine .partitionsCount ); i ++ {
202
225
topic := fmt .Sprintf (eventsTopic , i )
203
- x .eventStream .Subscribe (subscriber , topic )
226
+ engine .eventStream .Subscribe (subscriber , topic )
204
227
}
205
228
206
229
return subscriber , nil
207
230
}
208
231
209
232
// Entity creates an entity. This will return the entity path
210
233
// that can be used to send command to the entity
211
- func (x * Engine ) Entity (ctx context.Context , behavior EntityBehavior ) error {
212
- x .locker .Lock ()
213
- started := x .started .Load ()
214
- x .locker .Unlock ()
215
- if ! started {
234
+ func (engine * Engine ) Entity (ctx context.Context , behavior EntityBehavior ) error {
235
+ if ! engine .Started () {
216
236
return ErrEngineNotStarted
217
237
}
218
238
219
- x . locker .Lock ()
220
- actorSystem := x .actorSystem
221
- eventsStore := x .eventsStore
222
- eventStream := x .eventStream
223
- x . locker .Unlock ()
239
+ engine . mutex .Lock ()
240
+ actorSystem := engine .actorSystem
241
+ eventsStore := engine .eventsStore
242
+ eventStream := engine .eventStream
243
+ engine . mutex .Unlock ()
224
244
225
245
_ , err := actorSystem .Spawn (ctx ,
226
246
behavior .ID (),
@@ -237,11 +257,8 @@ func (x *Engine) Entity(ctx context.Context, behavior EntityBehavior) error {
237
257
// 1. the resulting state after the command has been handled and the emitted event persisted
238
258
// 2. nil when there is no resulting state or no event persisted
239
259
// 3. an error in case of error
240
- func (x * Engine ) SendCommand (ctx context.Context , entityID string , cmd Command , timeout time.Duration ) (resultingState State , revision uint64 , err error ) {
241
- x .locker .Lock ()
242
- started := x .started .Load ()
243
- x .locker .Unlock ()
244
- if ! started {
260
+ func (engine * Engine ) SendCommand (ctx context.Context , entityID string , cmd Command , timeout time.Duration ) (resultingState State , revision uint64 , err error ) {
261
+ if ! engine .Started () {
245
262
return nil , 0 , ErrEngineNotStarted
246
263
}
247
264
@@ -250,9 +267,9 @@ func (x *Engine) SendCommand(ctx context.Context, entityID string, cmd Command,
250
267
return nil , 0 , ErrUndefinedEntityID
251
268
}
252
269
253
- x . locker .Lock ()
254
- actorSystem := x .actorSystem
255
- x . locker .Unlock ()
270
+ engine . mutex .Lock ()
271
+ actorSystem := engine .actorSystem
272
+ engine . mutex .Unlock ()
256
273
257
274
// locate the given actor
258
275
addr , pid , err := actorSystem .ActorOf (ctx , entityID )
0 commit comments