I'm running an Azure service that host code in separate AppDomains. I need to handle service configuration changes in these AppDomains. For this purpose I'm listening to the RoleEnvironment.Changed event.
However, this event is only fired in the default AppDomain not in any other AppDomain. I can, however, get up-to-date values by calling RoleEnvironment.GetConfigurationSettingValue in all AppDomains.
Am I missing something, or is this a limitation by design?
I need to get this working so I'm thinking that I need to develop a workaround where only the default AppDomain listen to the Changed event and then communicate any changes to the other AppDomains using some basic IPC. Does anyone have a better idea for a workaround?
I've created a sample service to illustrate my problem:
public class WorkerRole : RoleEntryPoint
{
public override void Run()
{
AppDomain.CreateDomain("MySubdomain", null,
AppDomain.CurrentDomain.SetupInformation).DoCallBack(() =>
{
WorkerRole.EnableDevFabricTraceListener();
new Thread(WorkerRole.WorkerThreadProc).Start("CHILD");
});
WorkerRole.WorkerThreadProc("MAIN");
}
private static void WorkerThreadProc(object id)
{
Trace.TraceInformation(string.Format(
"[{0}] Starting", id), "Information");
RoleEnvironment.Changed += (sender, args) =>
{
Trace.TraceInformation(string.Format(
"[{0}] Role environment changed!", id), "Information");
};
string prevValue = null;
while (true)
{
string currValue = RoleEnvironment.GetConfigurationSettingValue(
"MySetting");
if (prevValue != currValue)
{
Trace.TraceInformation(string.Format(
"[{0}] MySetting={1}", id, currValue), "Information");
prevValue = currValue;
}
Thread.Sleep(1000);
}
}
// This is just a hack to enable tracing to the Azure Compute Emulator in
// other app domains.
private static void EnableDevFabricTraceListener()
{
try
{
const string className = "Microsoft.ServiceHosting.Tools.DevelopmentFabric.Runtime.DevelopmentFabricTraceListener";
const string assemblyName = "Microsoft.ServiceHosting.Tools.DevelopmentFabric.Runtime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
var assembly = Assembly.Load(assemblyName);
var computeEmulatorTraceListenerType = assembly.GetType(className);
var computeEmulatorTraceListener = (TraceListener)Activator.CreateInstance(computeEmulatorTraceListenerType);
Trace.Listeners.Add(computeEmulatorTraceListener);
}
catch
{
// suppressed
}
}
}
I'm running this service in the Azure Compute Emulator and use csrun.exe
to update the MySetting
value:
This is what I see in the compute emulator:
I've also tested this in a real deployment on Windows Azure with the same results.
I don't really see any reason for this (if it is by design), so I'm thinking that it might be a bug.
I've found that the internal method RoleEnvironment.InitializeCallbacks
, which is used to register callbacks for the Changed
event, is only invoked once per process and not once per AppDomain.
It is possible to invoke this method via the public RoleRuntimeBridge
class in Microsoft.WindowsAzure.ServiceRuntime.Implementation.Loader
by simply doing:
new RoleRuntimeBridge().Initialize(null);
However, only the last AppDomain to do this will have events fired. So while this enables me to get events fired in the "child" AppDomain, they are no longer fired in the default AppDomain.
The reason for this seem to be that the native infrastructure function WaRegisterCallback
only support one callback per process and callback type.
Any help or pointers would be appriciated!
I've filed a bug on Microsoft Connect for this issue.