By DevsrealmGuy
|
Published: 14 MAR, 2023
|
Updated: 16 MAR, 2023
Tonics is getting even more robust, in case you don’t know what it is, Tonics is a General Multi-Purpose Modular CMS, you can read more about its Architecture & Features (Quick Overview).
If you did read the Architecture and Features above and you like what you see, I am looking for sponsors or funds to further continue the development of the project, perhaps, if you have a job related to anything software engineering, architecture design or you just need an extra hand, you can reach out to me: olayemi@tonics.app or devsrealmer@gmail.com
Back to the guide…
In today’s guide on the Road To Release category, I am sharing some best practices and techniques I learned while working on the daemon that manages Tonics Job and Schedule Manager.
Let’s go…
Concerns About Using PHP for Long-Running Tasks
Many people believe that PHP is unsuitable for building daemons or long-running tasks because it is commonly used for web development and is often associated with generating dynamic web pages.
Some of the concerns people raise about using PHP for long-running tasks or daemon processes include issues with memory management (the garbage collector can sometimes lead to memory leaks, which can be problematic in long-running processes), resource consumption, and stability.
However, these issues can often be addressed through careful coding practices, proper use of system resources, and proactive monitoring and maintenance of running processes.
Another common criticism of using PHP for daemons or long-running tasks is that it is not as performant as other languages that are optimized for low-level system programming, such as C or Go. While there may be some truth to this assertion in certain use cases, the reality is that PHP can still be highly performant when used appropriately and optimized for specific tasks.
Furthermore, it’s worth noting that many developers may not fully understand the underlying principles of daemon and forking, and may simply follow the conventional wisdom of avoiding PHP in such scenarios.
Personally, I believe in investigating and experimenting with technologies to come to my own conclusions, which has saved me time and resources in the past, and some of my conclusions would be shared in this guide, the summary is, PHP can be a powerful tool for building long-running processes and daemons that are both efficient and reliable.
That aside, let’s look at some of the common mistakes and the best practices and or techniques you might deploy…
Common Mistake #1: Mis-Handling Forking
The pcntl_fork
is commonly use in long-running task to split some tasks into their own processes, this called forking, it splits the process into two identical but separate processes.
The child process runs a copy of the parent process code, but each process has its own memory space.
However, forking can be tricky and there are common mistakes people make that can cause issues in a long-running task or daemon. One such mistake is not properly handling the forking process, which can result in unexpected behavior or even crashes.
For example, file descriptors (and database connections) are shared between the parent and child processes after forking, if the parent or child closes a file descriptor or database connection, it will also be closed in the other process, potentially causing data corruption or other issues.
The above is not entirely true, but keep reading…
How pcntl_fork
works in PHP
Before showing an example, here is how pcntl_fork
works:
$pID = pcntl_fork();if ($pID === -1){
exit(1);
}// is this a parent process
if ($pID){
// continue the execution of parent task
}// is this a child process
if ($pID === 0){
// do...child task
exit;
}
The function pcntl_fork()
creates a copy of the current process, creating a parent process and a child process. After calling the pcntl_fork()
function, the $pID
variable will contain a different value in the parent and child processes.
Let me expand on the $pID variable containing different value in the parent and child processes…
When
pcntl_fork()
is called, it creates a copy of the current process. Here is where it gets interesting, both the parent and child processes continue executing the same code after thepcntl_fork()
function call, but the value of$pID
will be different in each process.In the parent process, $pID will be set to the process ID of the child process that was created, while in the child process, $pID will be set to 0.
This allows the code to differentiate between the parent and child processes and execute different code for each process.
To clarify, the pcntl_fork() function creates a new process and both the parent and child processes continue executing the same code from where the pcntl_fork() function was called.
The difference is that they have different values for $pID.
Back to the code block:
The first if statement checks whether the pcntl_fork()
function was successful. If it wasn’t, the program exits with an error code of 1.
The second if statement checks whether the value of $pID
is truthy in the parent process. If it is, the program executes the code inside the if block, which is the code for the parent task.
The third if statement checks whether the value of $pID
is 0 in the child process. If it is, the program executes the code inside the if block, which is the code for the child task.
The exit statement at the end of the child task ensures that the child process does not continue executing the parent code. How is that possible?
In the child process, after it executes the code for the child task, it will continue executing any code that comes after the if block where $pID is checked and found to be 0.
To prevent the child process from executing any more code from the parent process, the exit statement is used to terminate the child process after it has completed its task.
The exit statement causes the child process to stop executing immediately, so it does not continue executing any code that comes after it in the parent process.
Examples of How Forking Can Be Mis-Handled in PHP
There are a couple of good use cases for forking, one is if you want to run multiple processes at the same time, perhaps, speeding up some sort of task.
For example, let’s say you have a script that appends a line to a log file, and you want to run this script 10 times concurrently.
To achieve this, you can use the pcntl_fork()
function in PHP to create multiple child processes, each of which runs a separate instance of the script.
The child processes inherit a copy of the parent process’s memory, including open file descriptors, so they can all write to the same log file without interfering with each other.
Here is an example:
Note: I use the sleep(rand(…)) function to simulate the fact that some child would take some time to exit than others
$fp = fopen("test.log", "a");$pIDS = [];
for ($i = 0; $i < 10; ++$i) {
$pID = pcntl_fork();
if ($pID == -1) {
die("Could not fork process.");
} else if ($pID) {
// parent process