RELEASE_NOTES.md
with a new version bump. Follow the convention there.master
branch once all of the changes have been brought in.@Horusiath I am doing some test of an Actor that supposes to write into file upon receiving every event. The actor subscribes to the type of event to receive. However when I published N number of events to the event bus the actor only act on the first event by writing it into the file. The others are kind of ignored. This is the test code
ActorSystemRefs.ActorSystem = Sys;
var actorSystem = ActorSystemRefs.ActorSystem;
var loggingActor =
actorSystem.ActorOf(Props.Create(() => new FileLoggerActor()));
actorSystem.EventStream.Subscribe(loggingActor, typeof (AuditLog));
actorSystem.EventStream.Subscribe(TestActor, typeof(Acknowleged));
Within(TimeSpan.FromSeconds(timeout),
() =>
{
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit1", "mobile"));
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit2", "mobile"));
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit3", "mobile"));
var result = ExpectMsgAllOf<Acknowleged>();
Assert.NotNull(result);
});
and this is the Actor:
public class FileLoggerActor : ReceiveActor
{
private static string _filePath;
public FileLoggerActor()
{
Ready();
}
public override void AroundPreStart()
{
ReadConfiguration();
}
public sealed override void Ready()
{
Receive<AuditLog>(log =>
{
WriteToFile(log);
// This code is for testing purpose
ActorSystemRefs.PushEvent(new Acknowleged(log));
});
}
private static void ReadConfiguration()
{
_filePath = ConfigurationManager.AppSettings["FileLogger.FilePath"];
if (_filePath == null)
{
throw new ConfigurationErrorsException(
"Configuration appSetting FileLogger.FilePath not" +
"found for FileLogger actor.");
}
// If audit trail file path is a relative file path,
// convert it to an absolute path.
_filePath = _filePath.Replace("~", AppDomain.CurrentDomain.BaseDirectory);
}
private static void WriteToFile(AuditLog log)
{
var jsonString = JsonConvert.SerializeObject(log, Formatting.None);
File.AppendAllText(_filePath, jsonString + Environment.NewLine);
}
}
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit1", "mobile"));
ExpectMsg<Acknowleged>(x=> x.Property == "debit1");
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit2", "mobile"));
ExpectMsg<Acknowleged>(x=> x.Property == "debit2");
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit3", "mobile"));
ExpectMsg<Acknowleged>(x=> x.Property == "debit3");
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit1", "mobile"));
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit2", "mobile"));
actorSystem.EventStream.Publish(new AuditLog(GuidFactory.Create().ToString(), DateTime.UtcNow, "me", "0000", "debit3", "mobile"));
ExpectMsg<Acknowleged>(x=> x.Property == "debit1");
ExpectMsg<Acknowleged>(x=> x.Property == "debit2");
ExpectMsg<Acknowleged>(x=> x.Property == "debit3");
Hey all. I am exploring this "actors" thing, and had a question... in lookgin at Orleans and Akka, I want to make sure I am not fundementally makign a conceptual error. I am working on expanding the scalability and maintainability of a social application. There is already a DocumentDB record for each user with a class / object overlay that provides some linited interfaces to interact with it, a manager to instantiate it and so on.
Obviously, we have re-implemented (badly) somethign that others have done better :)
So does it make sense to put an Actor as a sort of smart cache / internaction model but still have the authoritative data for a user live in the documentDB rather than exist soley in the Actors persistent state? Or is that sort of half comitting to this.
@Horusiath - thanks, yeah, it sems pretty clear ideally the actor should be the sole interface to the underlying DB record. And that is with me. I just wont be able to seel converting our date purely to live within the actor state persistence store but I can se;; this.
The idea is that if, in the future, we need to interface with the data in some other way or move to another technology the data is not deeply tied to the actor implementation chosen.
This is the code:
public class ModulesActor : ReceiveActor
{
private Dictionary<string, Tuple<Props, ModuleState>> _moduleRecipes;
public ModulesActor()
{
Ready();
}
public override void AroundPreStart()
{
_moduleRecipes = new Dictionary<string, Tuple<Props, ModuleState>>();
}
public override void AroundPostStop()
{
_moduleRecipes = null;
}
protected void Ready()
{
Receive<LoadModules>(modules => Load());
Receive<ModulesLoaded>(c =>
{
_moduleRecipes = c.Modules;
});
Receive<GetModuleMeta>(whois =>
{
Sender.Tell(GetModuleMeta(whois.ModuleName));
});
Receive<CheckModuleState>(state =>
{
Sender.Tell(GetModuleState(state.ModuleName));
});
}
private ModuleState GetModuleState(string moduleName)
{
if (!_moduleRecipes.ContainsKey(moduleName))
return ModuleState.NOT_IMPLEMENTED;
return _moduleRecipes[moduleName].Item2;
}
protected override SupervisorStrategy SupervisorStrategy()
{
return new OneForOneStrategy(1,
// maxNumberOfRetries
TimeSpan.FromSeconds(30),
// withinTimeRange
exception => // localOnlyDecider
{
//Error that we cannot recover from, stop the failing actor
if (exception is NotSupportedException) return Directive.Stop;
// Error related to configuration setting
if (exception is ConfigurationErrorsException) {
return Directive.Escalate;
}
//In all other cases, just stop the failing actor
return Directive.Stop;
});
}
private ModuleMeta GetModuleMeta(string moduleName)
{
if (!_moduleRecipes.ContainsKey(moduleName)) return null;
var moduleMeta = new ModuleMeta(_moduleRecipes[moduleName].Item1,
_moduleRecipes[moduleName].Item2);
return moduleMeta;
}
private static void Load()
{
var loader = Context.ActorOf(Props.Create(() => new ModulesLoadingActor()));
loader.Forward(new LoadModules());
}
}
This is the test I am running:
public void ModulesLoaderActorShouldReturnALoadedModule()
{
ActorSystemRefs.ActorSystem = Sys;
var actorSystem = ActorSystemRefs.ActorSystem;
var moduleManager = actorSystem.ActorOf(Props.Create(() => new ModulesActor()));
moduleManager.Tell(new LoadModules());
var result = ExpectMsg<ModulesLoaded>();
Assert.NotNull(result);
moduleManager.Tell(new CheckModuleState("mtn-gh"));
var moduleMeta = ExpectMsg<ModuleMeta>();
Assert.NotNull(moduleMeta);
Assert.True(moduleMeta.State == ModuleState.ONLINE);
}
The issue I am facing is that the moduleMeta.State does not return the expected value. The first assertion works smoothly.