How to run Elixir Supervisor in escript - erlang-otp

I have a mix project with as simple as possible a Supervisor and GenServer. When I call from iex:
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
The :mumble call raises an exception, then the GenServer is restarted and the second :echo call works ok.
If I run the code in any other way the Supervisor fails to restart the GenServer. For example, I create an escript of the project with the main module as follows:
defmodule EchoCmd.Echo do
def main(args) do
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
end
end
The :mumble call raises an exception and the escript terminates without the Supervisor restarting the GenServer.
I've not included the Supervisor and Server modules code because they work fine when called from iex, so I'm guessing they're not needed here.
Do I have a conceptual misunderstanding? Is this not possible, or am I doing something wrong?

The problem lies not in your server and supervisor, but in the way you're calling them. If the server exits while another process is waiting for a reply to GenServer.call, the calling process exits too, so the last call never happens. The reason for this is the process couldn't possibly continue in an invalid state if a synchronous call failed (GenServer.call is synchronous as opposed to GenServer.cast). If you're doing this just to test the supervisor, then you can try:
defmodule EchoCmd.Echo do
def main(args) do
EchoCmd.Supervisor.start_link([:Hello])
GenServer.cast(:echoserver, :echo)
GenServer.cast(:echoserver, :mumble)
GenServer.cast(:echoserver, :echo)
end
end
The reason it works in iex is that iex traps the exit and allows you to type in another line.

Escript behaviour is correct. You just missing how iex shell "helps you".
What you are doing in your code is starting a linked process, and than crashing it. And since it is a linked process, when it goes down, it suppose to bring down all linked processes. There could be some "exceptions", but that what's happening to your escript process.
Both shell and prcess supervisor can handle such "I died, so should you" message. They do it by changing the way process (linked process, not the dying one) processes such messages. It allows them to receive they as normal messages (that you could receive in receive clause if you would like to) rather than special, internal ones. To change this bahaviour they use Process.flag( :trap_exit, :true) (elixir doc pointing to eralng's one). It allows shell to just print death of killed processes, rather that dying every time you do something bad.
So you could do same thing. Change this flag, and if you wan't pattern match in receive on such messages. But I don't think that's what you looking for. Since your process is singleton, and supervisor does all restarting, you don't really have any reason to link to it in first place. There is no need for updates on deaths and restarts, just let supervisor worry about that. It's just like Joe Armstrong said (might be paraphrasing)
You don't need to know how to fix vending machine to use it.
So just, start, rather than start_link.
That said, you might consider creating link with supervisor, which also might die after too many restarts (he might be told to behave in such way). And it allows you to take him (and supervised process) when you die. Or he could be linked to your supervisor, or application supervisor, or in some other manner. It depends on your domain, and there is no bad decision, you just have to check what's working for you. It is design decision, and you either have to experiment or read more about it:
http://elixir-lang.org/getting_started/mix_otp/5.html
http://www.erlang.org/doc/design_principles/des_princ.html
http://learnyousomeerlang.com/supervisors

Related

In testing with Elixir, can the dB connection assigned to a GenServer be reassigned to the testing one?

My system has an app under an umbrella which starts up several GenServers, which on init make some dB calls, and thus get their own connection.
The problem is that in my tests of a controller where I have inserted some records into the dB, the controller makes a call to one of those GenServers, which then does some more dB queries. But since it is a different sandbox, it does not see the records.
I am looking for a solution in general that would allow existing, running GenServers to use the same sandbox as the test. I would greatly appreciate any ideas. I tried adding the following right after the sandbox checkout in the test setup, but didn't help:
Ecto.Adapters.SQL.Sandbox.allow(MyApp.Repo, self(), Some.GenServer)
The only working solution I found was to allow the GenServer to be started with an alternate name, starting it up thus using the same sandbox db connection as the test, having my controller use this differently named version, and shutting it down right before the end of the test.
Seems that I ended up with the same issue.
My solution was to keep the default Phoenix settings, and terminate and restart my process before each test, like this:
setup(%{conn: conn}) do
Supervisor.terminate_child(Some.Supervisor, Some.GenServer)
Supervisor.restart_child(Some.Supervisor, Some.GenServer)
{:ok, conn: conn}
end
Tests SHOULD NOT be async: true

User exit for production order confirmation in CO11N?

I've watched quite a few videos on YouTube and have a basic understanding of how to find user-exits (enhancements?) and implement them. However when I try to replicate what I've seen it doesn't appear to be working.
I'm looking to create a user-exit that would execute when a production order has been confirmed (closed/finished) via CO11N. Someone suggested that I put in a line of code "BREAK username." So that I could verify that my code was firing. Nothing breaks. I've tried putting in a message from code found on the internet
MESSAGE s208(00) WITH 'TEST'.
No message is shown. I've activated the include and the project. I've tried different exits/includes and no matter what I do, nothing seems to break or show a message.
Is there something simple I'm missing? I've tried CONFPI05 and CONFPM05.
CONFPI05 is for process orders. CONFPM05 is for plant maintenance orders. First you need to check which kind of order you use. I assume you use production orders. You should check User-Exit CONFPP05 than.
Anyway, I would recommend using BAdI WORKORDER_CONFIRM. Within this BAdI there are methods available where you can raise an error message.
From the BAdI documentation:
Note that in the methods, no system messages may be sent. The only
exceptions are the AT_SAVE and AT_CANCEL_CHECK methods. Within these
methods, a system message may be issued, but only if you trigger the
exception ERROR_WITH_MESSAGE (for AT_SAVE method) or NOT_ALLOWED (for
AT_CANCEL_CHECK method) at the same time.
Note also that within the methods, the "commit work" instruction may
not be carried out because this would lead to incorrect data in the
database.
I strongly recommend not to use MESSAGE statement in any User-Exit or BAdI implementation. The MESSAGE statement will implicit call a COMMIT WORK which could cause database inconsistencies (happens very often by the way).
One additional note. You should check using Checkpoint Groups instead of using BREAK-POINT or BREAK username directly.
I checked the documentation:
CONFPI05 to update your own data after saving the confirmation
In another documentation I found another warning:
In this customer enhancement it is strictly forbidden to send error messages or other messages because otherwise there is the danger that data will be inconsistent. SAP cannot be held responsible for this!!
This sounds like changes in update task. By default breakpoints in update task are not enabled.
Should your code be processed after you pushed save?
If yes, what you can try:
Set anywhere a breakpoint. Or try /h during data insertion.
In debug screen activate the update debugging:
Continue the process with F8.
Hopefully you stop at your break-point.

Application.restart - Puzzling behaviour in VB.Net

OK guys, what's going on here?
In this VB code:
Module Module1
Sub Main()
If MsgBox("Restart?", MsgBoxStyle.OkCancel) = MsgBoxResult.Ok Then
Application.Restart()
MsgBox("restarting")
Else
MsgBox("Cancel")
End If
End Sub
End Module
If this code is contained within a module, Application.Restart does not end the running application till the End Sub is hit. Any code that appears before then is executed - eg the 'Restarting' messagebox appears.
However, if the equivalent code is run within a form then Application.Restart terminates the running application immediately.
(Both cases correctly start a new instance). This behaviour does not appear to be documented anywhere - the implication in the docs is that it's synonymous with 'End' as far as the termination of the running instance is concerned. Am I missing something?
The best way to answer these questions it to look at the code itself using Reflector (or Microsoft's free for debugging code, when it is available).
With Reflector, you can see (in .NET Framework 4.0) System.Windows.Forms.Application.Restart looks for four different types of applications:
the initial check that Assembly.GetEntryAssembly is Nothing, throwing a NotSupportedException if it is;
the Process.GetCurrentProcess.MainModule.FileName is ieexec.exe in the same folder as the current .NET Framework (specifically the folder where the module defining Object is);
ApplicationDeployment.IsNetworkDeployed is True; and
the general case.
All three supported cases determine the method to start the process again, calls Application.ExitInternal and starts the process again.
Application.ExitInternal closes open forms, including the check for a form attempting to abort the close by setting FormClosingEventArgs.Cancel to True. If no form attempts to cancel, the forms are closed and, using ThreadContext.ExitApplication, all ThreadConnexts are cleaned up (Disposed or their ApplicationContext.ExitThread is called).
NB No Thread.Abort is called, so threads are NOT explicitly ended in any way. Also the Windows.Forms ModalApplicationContext, does not even call the ThreadExit "event" that a normal ApplicationContext does.
(Note that all three supported cases in Application.Restart ignore the result of Application.ExitInternal, so if a form does attempt to abort all that happens is any other forms don't get a chance to close, and the ThreadContexts are not cleaned up!)
Importantly for your question, it does NOT attempt to actually exit the current threads or the entire application (other than closing open forms and thread contexts).
However, by the time your MsgBox("restarting") executes the new application has been started.
You need to manually exit the application after calling Application.Restart. In the case of "run[ing] within a form" (you don't show the code where you tested this) either the form is closed and that is what you considered as the current application ending, or extra stuff that Windows.Forms (or VB) sets up means the application is exited by one of the "events" that throw when the clean up that does occur runs.
In other words, before testing it I expected the MsgBox to appear even when this code is in say the Click event of a form, with the form disappearing first, and the application restarting at the same time.
Having tested it, the MsgBox tries to appear, as I hear the beep that corresponds to it, and if I comment it out the beep does not occur. So something causes the application to exit even though it should have a message box open, and even putting a MsgBox in a Finally outside of the Application.Run does not appear on a Restart. (Note a similar effect is seen if you call MsgBox after Application.Exit.)
So something set up by Windows.Forms (or VB) does actually call something like Environment.Exit which calls the Win32Api ExitProcess and does not regard Finally or call Dispose or Finalize.
Note the Application.Restart documentation implies it is not for Console Applications though it currently works fine (except for the not quitting straight away, which is not implied by Application.Exit).
I am able to restart the application by closing and disposing all open forms, except the one that is calling.
For j As Integer = Application.OpenForms.Count - 1 To 0 Step -1
Dim frm = Application.OpenForms(j)
If frm.Text <> callingForm.Text Then
frm.Close()
frm.Dispose()
End If
Next
Application.Restart()
This is going to be, admittedly, a bit of a guess based on some fairly top-level reading I've done about Application.Restart(), but I think this is occurring due to the way Restart operates internally.
I think Restart() tries to do as much "intelligent" cleanup as it can for a process that is being terminated, and in what may be considered a fairly simplistic implementation, tracks certain of the things to be "cleaned up," possibly calling Dispose() on them (if applicable), which normally is a reasonable step to take. In your case, I'm going to make the guess that a background thread, or form, holds a reference to something - can't say what - that prevents the code from shutting down. It may become aware that it is executing inside a method, and wants to give that method a chance to complete before killing it - waiting on the completion of that sub/method.
I've seen other instances of Restart actually causing a really strange "Collection was Modified" error when no collection was involved. That's suggesting to me, probably naively, that the internal cleanup Restart is trying to achieve is reposed in a simple list, but in certain circumstances, the cleanup modifies the element in an unexpected way, a way that modifies the collection, causes the exception to be thrown, and aborts the exit/restart.

Erlang/OTP, how to signal an application startup error without a crash

So I have this application that has a process that requires some gen_servers to be alive somewhere else in the cluster.
If they are up it just works, if they are not, my gen_server fails in init with {error,Reason}, this propagates through my supervisor into my applications start function.
The problem is that if I return anything other than {ok,Pid} I get a crash report.
My intention here would be to somehow signal that the application couldn't start properly and that all the processes are down and because of that the application should not be considered active, however, I can only choose to return {ok, self()} and see my application listed as active when it is not, or return {error, Error} and see how it crashes with:
{gen_server,init_it,6},{proc_lib,init_p_do_apply,3}]}},{ancestors,[rtb_sup,<0.134.0>]},
{messages,[]},{links,[<0.135.0>]},{dictionary,[]},{trap_exit,false},{status,running},
{heap_size,377},{stack_size,24},{reductions,255}],[]]:\n"
The problem seems to be bigger than this, basically there is no way to tell to the application framework that the app failed. It may look like one of these things that are handled by let the process die in erlang, but allow for an {error, } return value on application:start seems like a good tradeoff.
Any hints?
Application will crash at any moment, so application's dependence relationship at the start time can not provide helpful dynamic crash information.
Before I have read part of rabbitmq project source code, it is also a cluster-based program.
I think rabbitmq has faced your similar question as you said, because cluster need collect related nodes's application "is live" information and memory water highmark information and then make decision.
It's solution is
to register the the first main process of the application in the node locally, the name is "rabbit" in the rabbitmq system, you can find it is rabbit.erl file, and in the function "start/2".
start(normal, []) ->
case erts_version_check() of
ok ->
{ok, SupPid} = rabbit_sup:start_link(),
true = register(rabbit, self()),
print_banner(),
[ok = run_boot_step(Step) || Step <- boot_steps()],
io:format("~nbroker running~n"),
{ok, SupPid};
Error ->
Error
end.
And the other 4 modules, rabbit_node_monitor.erl, rabbit_memory_monitor.erl,
vm_memory_monitor.erl, rabbit_alarm.erl to use two erlang technique, one is monitor process to get "DOWN" message of the registered process, the other is alarm handler to collect these information.

Hooking TerminateProcess & Getting Info From The Handle It Supplies

If you want to stop a process from being terminated, one way is to hook into TerminateProcess (or NtTerminateProcess). If the process is terminating itself (because you closed its window, for example), the handle supplied to those functions is NULL, so you can find out what executable is being terminated using GetCurrentProcess() & GetModuleFileNameEx(). As GetCurrentProcess() returns a pseudo-handle, you can access it with no problems.
If one process is terminating another, though, the handle supplied is not NULL. It represents the process being terminated. The problem is, you can't get information about that process. You can simply return a code saying "access denied" instead of calling the original [Nt]TerminateProcess(), but that blanket stops all processes from terminating others - which is a bad idea.
The handle must represent something valid otherwise TerminateProcess wouldn't be able to do anything useful with it - but I can't even call GetProcessId() on it, I get ERROR_INVALID_HANDLE (or ERROR_ACCESS_DENIED). I've tried various methods I've collected from the help and from online, including gaining the debug privilege (success) and DuplicateHandle() (same error) and ZwQueryInformationProcess() to get the ID (STATUS_ACCESS_DENIED). I can't even enumerate processes because they return IDs, and I can't get the ID, and OpenProcess() always returns a fresh handle, so I can't compare handles.
I can only assume the handle has PROCESS_TERMINATE right and nothing else. I know that Vista and higher have protected processes due to Digital Rights Management, but I'm using ProcessExplorer as my guinea pig so it's definitely not a media application!
Does anyone know how else I might be able to get any kind of information about the process being terminated from this handle?
It's just an ordinary process handle. The question is, in which process is your hook function executing? If it's the calling process, the handle can be used as-is for GetProcessId or NtQueryInformationProcess. If not, you need to call DuplicateHandle to duplicate the handle into your process.
If you're getting access denied errors, it may be because the process handle only has PROCESS_TERMINATE access. In that case, use DuplicateHandle to "re-open" the process with PROCESS_QUERY_(LIMITED_)INFORMATION access.