Skip to content

Commit

Permalink
docs improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
vladopajic committed Feb 9, 2024
1 parent a148b0a commit b7b48d3
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 8 deletions.
62 changes: 55 additions & 7 deletions docs/best_practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,66 @@ While the general rule is to avoid `sync` package usage in actor-based code, the
Workers should always respond to `Context.Done()` channel and return `actor.WorkerEnd` status in order to end it's actor. As a rule of thumb it's advised to always list this case first since it should be included in every `select` statement.

```go
func (w *worker) DoWork(ctx actor.Context) actor.WorkerStatus {
func (w *fooWorker) DoWork(ctx actor.Context) actor.WorkerStatus {
select {
case <-ctx.Done(): // <----------------------- handle ctx.Done() first
return actor.WorkerEnd
case msg := <-w.mbx.ReceiveC():
handleFoo(msg)
}
}
```

## Check channel closed indicator in `DoWork`

Every case statement in `DoWork` should handle case when channel is closed. In these cases worker should end execution; or it can perform any other logic that is necessery.

```go
func (w *fooWorker) DoWork(ctx actor.Context) actor.WorkerStatus {
select {
case <-ctx.Done():
return actor.WorkerEnd
case <-w.fooMbx.ReceiveC():
handleFoo()
case <-w.barMbx.ReceiveC():
handleBar()
case msg, ok := <-w.mbx.ReceiveC():
if !ok { // <----------------------- handle channel close (mailbox stop) case
return actor.WorkerEnd
}
handleFoo(msg)
}
}
```


## Combine multiple actors to singe actor

`actor.Combine(...)` is vary handy to combine multiple actors to single actor.

```go
type fooActor struct {
actor.Actor
mbx actor.Mailbox[any]
...
}
func NewFooActor() *fooActor {
mbx := actor.NewMailbox[any]()
a1 := actor.New(&fooWorker{mbx: mbx})
a2 := actor.New(&fooWorker{mbx: mbx})
return &fooActor{
mbx: mbx,
Actor: actor.Combine(mbx, a1, a2).Build() // <------- combine all actors to single actor and initialize embeded actor of fooActor struct.
} // when calling fooActor.Start() it will start all actors at once.
}
func (f *fooActor) OnMessage(ctx context.Context, msg any) error {
return f.mbx.Send(ctx, msg)
}
```

---

`// page is not yet complete`
This page is not yet complete.
30 changes: 29 additions & 1 deletion docs/common_hurdles.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,34 @@ One of the most common hurdles is the case where actors are not started. This is

Never forget that `actor.Mailbox` is also an actor, and it needs to be started.

**Embeded Actor interface is overriden**

When embeding `actor.Actor` interface make sure not to override methods of this interface in structre that has embeded it. Otherwise make sure to call embeded actor's Start() and Stop() methods.

```go
type fooActor struct {
actor.Actor
...
}

func NewFooActor() *fooActor {
return &fooActor{
Actor: actor.New(&fooWorker{...})
...
}
}

func(f *fooActor) Start() { // <--- warning: calling fooActor.Start() will override fooActor.Actor.Start() method.
... // therfore calling this method will not execute worker that was itended
} // to be excuted with fooActor.
// if this method is necessery then make sure to call `f.Actor.Start()` manually here.

func(f *fooActor) Stop() { // <---- similar problem as described above.
...
}

```

## Default case is undesirable

Workers should always block when there isn't anything to work on; therefore, their `select` statements shouldn't have a `default` case. If workers do not block, they will simply waste computation cycles.
Expand All @@ -32,4 +60,4 @@ func (w *consumeWorker) DoWork(ctx actor.Context) actor.WorkerStatus {

---

// Your contribution is valuable; if you have encountered any challenges, please share your experiences.
Your contribution is valuable; if you have encountered any challenges, please share your experiences.

0 comments on commit b7b48d3

Please sign in to comment.