-
Notifications
You must be signed in to change notification settings - Fork 581
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
Significantly reduce file descriptors consumption #3085
base: master
Are you sure you want to change the base?
Conversation
of the socketpair in child after forking. This results in 30% decreate of the total number of sockets allocated, from 8k to some 5.8k on my test config with 10 workers and 10 sockets.
Hi @sobomax , thanks for tackling this issue. I agree that things may be improved here, that some file descriptors may be closed in vary processes. BUT, I'm not convinced (yet to do deeper thinking on the code here) that the implementation is correct. In the |
@bogdan-iancu thanks for a review! Please re-read the detailed description above. The answer to your question is that the child proc does "dup2(read_env_fd, write_env_fd), that would make "read end" FD the same as "write end" (and close original "write end" in child in the process). That's how we get away with using the same FD on both read and write side. :) |
Just for posteriority, update here. The problem with the patch right now turned out to be some rare and somewhat hacking use of the IPC API by a few modules, which use it to get code executed by the child process after initialization has been completed. This basically breaks pattern, so that the code running in the child is trying to make IPC call to itself. This becomes impossible, since with this patch child process no longer has access to the write end of the IPC pipe. :-(
This has the effect of having ul_rpc_data_load() call to be dispatched after reactor starts. I am considering few options to get this resolved. Simplest one, perhaps, would be to detect this condition in the ipc_send_rpc() and just put that call into the incoming call queue to be dispatched without doing any real I/O on the socket. |
Summary
Significantly reduce file descriptors consumption (by 30-50%). From 8,000 to 5,800 using the example below.
Details
Reduce number of sockets created by closing off one half of the socketpair in child and parent after forking. This results in 30% decrease of the total number of sockets allocated, from 8k to some 5.8k on my test config with 10 workers and 10 sockets.
Usual scenario involving shared pipe / socketpair in parent / child in POSIX / Linux is that two descriptors returned by the system call are then split with first descriptor used by parent and then the second is closed off by parent and the second used by a child and the first is closed by the child (or vice versa). OpenSIPS breaks this rule by keeping both descriptors open in both parent and child, effectively wasting up descriptor space. This could lead to hitting a system-wide limit or some excessive system load due to enormous size of the descriptor table.
Solution
pt[N].ipc_pipe[2] is replaced with just a single int. Initially set to be the parent's end of the pipe, in the child after fork() we do dup2 of the second fd using parent's end as a destination. This closes master's end (in child) and ensures FD numbers for IPC are the same in both parent and child, so that pt[N].ipc_pipe does not need to differ (helping with debugging for example). Then the original child's pipe is closed off in child. In master we simply close the child's end once fork is done.
Compatibility
As it only has access to read-only end of the pipe.
This seems to be copied and pasted into other places too:
The solution might be to create special API to request sending a wake up API at module creation time. Or perhaps have a ping-pong RPC API? The module would just call main proc and get it to return the call ASAP?
OpenSIPS Configs Tested
opensips1.cfg
opensips2.cfg