Skip to content
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

Race Condition when Starting Actor #159

Closed
troygilman0 opened this issue Jun 11, 2024 · 0 comments
Closed

Race Condition when Starting Actor #159

troygilman0 opened this issue Jun 11, 2024 · 0 comments

Comments

@troygilman0
Copy link
Contributor

Hello, I believe I have found a race condition that occurs when the handling of the actor.Initialized or actor.Started message takes a significant amount of time (ex. loading state from db). In the example below, I demonstrate how if I sleep for 1 second during the actor.Started message, the message in the actor context is actually swapped for main.testMsg in the middle of handling the message. This is caused by the lack of synchronization between the startup sequence (actor.Initialized, actor.Started) with the handling of any other messages sent to the actor.

package main

import (
	"fmt"
	"log/slog"
	"time"

	"github.com/anthdm/hollywood/actor"
)

func main() {
	engine, err := actor.NewEngine(actor.NewEngineConfig())
	if err != nil {
		slog.Error(err.Error())
	}

	go func() {
		engine.Spawn(newServer, "server", actor.WithID("primary"))
	}()

	time.Sleep(100 * time.Millisecond)

	pid := engine.Registry.GetPID("server", "primary")
	engine.Send(pid, testMsg{})

	time.Sleep(5 * time.Second)
}

type testMsg struct{}

type server struct {}

func newServer() actor.Receiver {
	return &server{}
}

func (s *server) Receive(ctx *actor.Context) {
	switch ctx.Message().(type) {
	case actor.Started:
		time.Sleep(time.Second) // simulate loading state from database
	}
	slog.Info(fmt.Sprintf("%T", ctx.Message()))
}

As you can see below, main.testMsg is incorrectly printed twice. The expected behavior would be to print actor.Initialized, actor.Started, and then main.testMsg.

$ go run .
2024/06/11 16:45:44 INFO actor.Initialized
2024/06/11 16:45:44 INFO main.testMsg
2024/06/11 16:45:45 INFO main.testMsg

I do this sort of asynchronous spawning and sending of messages to actors in my application quite often so this is causing real issues when attempting to load state. I propose some sort of synchronization between process.Start() and process.invokeMsg() to prevent an actor from receiving more than one message at a time. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants