ChiroHeal – Chiropractor Physiotherapy Wellness WordPress Theme nulled

Fix PHP session locks on wellness site

1. The Slow Response Symptom

I manage a server for a health clinic. The clinic uses a specific site design. They use the ChiroHeal – Chiropractor Physiotherapy Wellness WordPress Theme. This theme has many parts. It has a booking system. It has a patient portal. The site felt slow yesterday. I do not mean it crashed. I mean it paused. A user clicked a button. The button stayed down. The page took five seconds to load. I checked the CPU. The CPU was idle. I checked the RAM. The RAM was free. This was a silent problem. I needed to find it.

I logged in with SSH. I used a private key. I am the only admin. I went to the site folder. I looked at the logs. I saw the access log. The web server was Nginx. Nginx said the status was 200. But the time taken was high. Some requests took 5.0 seconds. This is a common timeout for PHP. I knew the issue was in the PHP engine.

2. Checking the Journal Logs

I needed to see what PHP was doing. I used the system journal. I typed journalctl -u php8.1-fpm --since "1 hour ago". The screen showed many lines. I read every line. I saw a warning. It appeared every few minutes. The warning said "script timeout". It mentioned a specific file path. The path was in the sessions folder.

The logs are helpful. They tell the truth. I saw a line about a file lock. It said "flock() failed". The error code was "EAGAIN". This means the resource is busy. Something else was holding the file. In PHP, this usually means a session lock. One user opens two pages. The first page locks the session. The second page waits. If the first page is slow, the second page hangs.

3. Using Lsof to Trace Handles

I needed to find the busy files. I used the lsof tool. This tool lists open files. I typed lsof -u www-data. This shows files opened by the web user. The list was very long. I saw many socket files. I saw many library files. I looked for the session files. They are in /var/lib/php/sessions/.

I saw one file with many handles. The file name was sess_u7d8.... Ten PHP processes were looking at this one file. One process had it open for writing. Nine processes were waiting for a lock. This was the bottleneck. One user was blocking themselves. I checked the process IDs. I typed ps -p 4501,4502,4503 -o comm,args.

The processes were running the ChiroHeal theme. Specifically, they were running a script for patient updates. When users Download WordPress Themes, they often get many features. These features use sessions. If the code is not clean, the session stays open too long. I needed to see why the lock did not release.

4. Understanding File Locks

The Linux kernel handles locks in a specific way. There are two types of locks. The first type is an advisory lock. The second type is a mandatory lock. PHP uses advisory locks with the flock call. This means other processes can still read the file if they do not ask for a lock. But PHP asks for a lock on every session start.

I looked at the /proc/locks file. This file shows every lock in the system. I typed cat /proc/locks. I saw a row with the word FLOCK. I saw the word WRITE. I saw the PID of the owner. The PID was 4501. This process was the leader. It held the key. The other processes were in a queue. They could not move until 4501 finished its work.

5. Finding the Code Leak

I needed to see the code in the theme. I used grep to find session calls. I typed grep -r "session_start" .. I found the call in a file named appointment-ajax.php. This file handles the booking form. I opened the file. I read the code. It was simple. It started the session. Then it called an external API.

The API was for a health insurance check. The API was slow. Sometimes the API took four seconds to answer. While PHP waited for the API, the session stayed locked. The user clicked the button again. A new request started. The new request waited for the first request. This created a chain of waiting.

6. The Virtual File System Limit

Every process has a limit of open files. I checked the limit for the web user. I typed cat /proc/4501/limits. The Max open files was 1024. This is a soft limit. If a process leaks handles, it hits this limit. When it hits the limit, it cannot open the database. It cannot open logs. It just dies.

I checked the current count. I typed ls /proc/4501/fd | wc -l. The count was 50. This was not a total leak. It was a time leak. The file was not "lost". It was just "busy". I needed to tell PHP to let go of the file sooner.

7. Fixing the Session Logic

I found the fix in the PHP manual. There is a function called session_write_close(). This function saves the data and releases the lock. The script continues to run after this call. But it no longer blocks other requests. This is the correct way to handle slow APIs.

I edited the file. I moved the session call. I put session_start() at the top. I read the data I needed. Then I added session_write_close(). After that line, I kept the API call. Now, the slow insurance check happens without a lock. The user can open other pages while they wait.

8. Verifying the Fix

I saved the file. I cleared the old session files. I typed rm /var/lib/php/sessions/sess_*. I restarted the PHP service. I typed systemctl restart php8.1-fpm. I went to the site. I opened the booking form. I clicked the button ten times.

I went back to the terminal. I typed lsof -u www-data | grep sess | wc -l. The count was 1. Only the current request had a handle. The other requests finished and closed their handles. I checked /proc/locks. There were no long-running write locks. The server was smooth again.

9. Tuning the Session Garbage Collector

I noticed many old files in the session folder. Some were three days old. The system should clean these. I checked the PHP config. I typed grep "gc_" /etc/php/8.1/fpm/php.ini. The session.gc_probability was 0. This is the default on Debian. Debian uses a cron job instead.

I checked the cron job. I typed cat /etc/cron.d/php. The job runs every 30 minutes. It looks for files older than the max lifetime. I looked at the max lifetime. It was 1440 seconds. That is 24 minutes. The cron job was working. But it only runs every 30 minutes. If the site is busy, 30 minutes of files can be 10,000 files.

I decided to increase the frequency. I changed the cron to run every 10 minutes. I also changed the session.save_path. I moved it to a subfolder. This makes the ls command faster. If a folder has 50,000 files, the kernel slows down when reading the directory.

10. File Descriptor Management in PHP-FPM

I checked the PHP-FPM pool config. I typed nano /etc/php/8.1/fpm/pool.d/www.conf. I looked for rlimit_files. It was commented out. I removed the semicolon. I set the value to 4096. This gives the workers more room. If a small leak happens, the server will stay up longer.

I also looked at process.max. I set it to 100. I do not want too many workers. Too many workers will eat all the RAM. If each worker uses 50MB, 100 workers use 5GB. This server has 8GB. That is a safe margin.

11. Monitoring with Journalctl Flags

I learned some new flags for journalctl. I used -f to watch in real time. I used -n 100 to see the last 100 lines. I also used -p err to see only errors. This filters out the noise. I saw no new lock errors. I saw no "resource busy" messages.

I set up a small script. The script runs every minute. It checks the number of locked files. If the number is more than 20, it sends me a mail. I used grep and wc for this. It is a simple way to stay safe.

12. The VFS Cache and Performance

I checked the kernel cache. I typed cat /proc/sys/vm/vfs_cache_pressure. The value was 100. This is standard. It tells the kernel how to cache file data. If I increase it, the kernel clears the cache faster. I kept it at 100. The RAM is enough to hold the file metadata.

I used free -h. The buff/cache was 2GB. This is good. It means the kernel remembers the session files. It does not need to hit the physical SSD every time. This makes the site faster for the users.

13. Handling Stale Locks

Sometimes a process dies but the lock stays. This is rare with flock because the kernel clears it when the file handle closes. But it can happen with network files. I checked if this site used NFS. It did not. It used local EXT4. Local files are safer for sessions.

I checked the wp-config.php for the theme. Some themes use database-based sessions. I checked the ChiroHeal – Chiropractor Physiotherapy Wellness WordPress Theme settings. It was using the default PHP handler. This is why my fix worked. If it used a database handler, I would have checked the MySQL process list.

14. Conclusion of the Audit

The problem was a classic race condition. The slow API kept the lock. The user kept clicking. The handles leaked until the timeout. By using session_write_close(), I broke the chain. The server is now stable. The patient portal is fast.

I updated my notes. I wrote down the file name and the line number. I will check this on other medical sites. Many clinics use similar designs. They all have the same risk.

15. Final Check on File Permissions

I checked the session folder permissions. I typed ls -ld /var/lib/php/sessions. It was drwx-wx-wt. The owner was root. The group was root. The t at the end is the sticky bit. This means only the owner of a file can delete it. This is a secure setup.

I looked at the files one last time. They were all owned by www-data. The sizes were small. Most were 1KB. Everything looked normal. I closed the terminal. My work was done.

16. Technical Details of Flock in Linux

I want to explain how flock works inside the kernel. When a process calls flock(fd, LOCK_EX), the kernel checks the inode structure. Every file on the disk has an inode. The inode has a list of locks. If the list is empty, the kernel adds the lock. It attaches the PID to the lock entry.

If the list has an exclusive lock, the process sleeps. The kernel puts the process in the "Wait" state. This state does not use CPU. But it uses a process slot. If 100 processes are in the Wait state, the server hits the max_children limit. This is why the site felt "dead" but the CPU was "idle".

I checked the wait queue. I typed cat /proc/loadavg. The first number was 0.05. The second number was 5.00. The high second number shows the history of the wait. Now it is back to 0.05. This is the goal.

17. Memory Usage of File Descriptors

Each file descriptor uses a small amount of kernel memory. It is about 1KB. On a big server, this is nothing. But the total limit of the system is important. I checked it. I typed cat /proc/sys/fs/file-max. The number was 780,000. I will never hit that. The per-process limit is the real enemy.

I checked the memory used by PHP-FPM. I typed ps -u www-data -o rss,command | awk '{sum+=$1} END {print sum/1024 " MB"}'. The total was 450MB. This is very good for 100 workers. Each worker is light.

18. Impact on Insurance API Calls

The clinic owner asked about the insurance check. I explained the fix. I told him the check is still slow. But now it does not hurt the rest of the site. He can keep using the insurance service. He does not need to find a new one.

I checked the theme logs. The theme has a debug mode. I turned it off. I typed nano wp-config.php. I set WP_DEBUG to false. Debug mode writes to the disk. More disk writes mean more locks. I want to keep the disk quiet.

19. Session ID Generation and Entropy

I checked the security of the sessions. I typed grep "session.sid" /etc/php/8.1/fpm/php.ini. It was using 32 characters. The entropy source was /dev/urandom. This is the standard for Linux. It is fast and secure.

I checked the system entropy. I typed cat /proc/sys/kernel/random/entropy_avail. The value was 3500. This is plenty. If it goes below 200, the server slows down when generating random numbers. This happens on old servers without hardware random generators. This server is new.

20. Final System Load Verification

I ran a stress test. I used the ab tool. I typed ab -n 1000 -c 50 https://site.com/. This sends 1000 requests with 50 at a time. The test finished in 10 seconds. The requests per second was 100. This is excellent for a WordPress site.

I monitored the handles during the test. I used a loop. while true; do lsof -u www-data | grep sess | wc -l; sleep 1; done. The count never went above 5. This proves the session_write_close() fix is perfect. The handles open and close instantly.

21. Understanding Inode Tables

I looked at the disk inodes. Every file needs an inode. I typed df -i. The usage was 5 percent. Some servers with many sessions run out of inodes. If you have millions of small files, the disk says it is "full" even if there is space.

I told the owner to keep the current SSD. He does not need more space. He only needs better code. He thanked me for the work. I wrote a final report.

22. Kernel Logs and OOM Killer

I checked the kernel ring buffer. I typed dmesg | tail. I saw no errors. I looked for the "Out of Memory" (OOM) killer. I typed dmesg | grep -i "oom". There were no results. This means the server never ran out of RAM.

When the session handles were leaking, the memory usage stayed stable. This was a "wait" problem, not a "memory" problem. Wait problems are harder to find because they do not trigger alarms. You must look at the process states.

23. Analyzing the PHP Slow Log

I had enabled the slow log earlier. I typed cat /var/log/php-fpm/www-slow.log. I saw the trace of the appointment-ajax.php script. It showed the curl_exec function as the slow part. This is the API call.

The log confirms my diagnosis. The script spent 99 percent of its time waiting for the insurance server. Now that the session is closed before that wait, the site is free. This is a textbook example of "non-blocking" logic in a blocking language.

24. Future Proofing with Redis

I suggested using Redis for sessions in the future. Redis is a memory-based store. It does not use files. It does not use the kernel lock system. It handles locks in RAM. This is much faster.

If the site grows, Redis is the next step. For now, the file system is enough. I keep things simple until they need to be complex. Simple is easy to debug.

25. Checking System Uptime and Stability

I typed uptime. The server was up for 24 hours. The load was 0.02. I looked at the history. I used sar -q. The load peaks were gone. The server is quiet and cold. Just the way I like it.

26. Final Verification of Category and Themes

I reviewed the other themes the client bought. He likes to Download WordPress Themes for his other projects. I checked the code of two other themes. They were clean. They did not use the same insurance API. So they did not have the same bug.

Each theme is different. A site administrator must check each one. You cannot trust default code. You must watch the files. You must watch the handles. You must watch the locks.

27. Summary of Changes

I wrote a list for the client. 1. Identified file handle bottleneck. 2. Verified lock contention in /proc/locks. 3. Modified appointment-ajax.php to release session early. 4. Increased worker file limits to 4096. 5. Optimized session cleanup frequency.

The site is now robust. I am ready for the next ticket.

28. Impact of Gzip on Session Files

I checked if the session files were large. If a session is 1MB, it takes time to read and write. I checked the theme data. It only saves the user ID and a few strings. The average file size was 200 bytes. This is very small.

Small files are fast. They fit in one disk block. They fit in the CPU cache. Gzip is not needed for session files. It would just use more CPU. I left the compression off.

29. Database Connection Pooling

I checked if the theme was leaking database connections. I typed mysqladmin processlist. I saw 5 active connections. This is normal. PHP-FPM workers use persistent connections. This avoids the handshake overhead.

I checked the wait time for SQL. It was 0.001 seconds. The database is not the problem. The API was the problem. I am glad I did not waste time tuning the database. I followed the evidence.

30. Verifying the Path of Session Files

I checked the phpinfo() output. I typed php -r "phpinfo();" | grep session.save_path. It matched my manual check. It is important to verify. Sometimes there are multiple php.ini files. One for CLI and one for FPM. I checked both. Both were correct.

I am satisfied with the audit. The server is in top shape. The clinic can serve its patients. I can go back to my other servers.

31. Handling Large PHP Sessions

I considered what happens if a session becomes huge. In some themes, developers save large arrays in the session. This is bad practice. It slows down every page load. I checked the ChiroHeal – Chiropractor Physiotherapy Wellness WordPress Theme for this. It does not do this. It only saves small tokens.

If I found large sessions, I would have changed the code to use the database for large data. But here, the file-based system is perfect. It is fast and simple.

I searched the web folder for broken links. I typed find . -type l -xtype l. There were no results. Broken links can cause some "File Not Found" errors in the logs. These errors fill up the disk and waste CPU. The folder is clean.

33. Reviewing Kernel Network Stack

I checked the TCP settings. I typed sysctl net.ipv4.tcp_fin_timeout. It was 60. I changed it to 30. This closes idle connections faster. It frees up more sockets for new users. This is a small change but good for health sites with many visitors.

I also checked net.core.somaxconn. It was 128. I increased it to 1024. This allows the web server to handle more waiting connections in the queue.

34. Monitoring Disk Latency

I used the iostat tool. I typed iostat -xz 1 5. The %util was 0.5. The disk latency was 0.1ms. The SSD is very fast. The hardware is not a limit. The only limit was the software lock. Now that it is gone, the disk can breathe.

35. Final Audit of Sudo Logs

I checked who used the sudo command recently. I typed grep sudo /var/log/auth.log. Only my username was there. This means no other scripts or people changed the system. Security is about knowing every change.

36. Verifying Time Synchronization

I checked the server time. I typed timedatectl. The NTP service was active. The time was synchronized. Accurate time is vital for session cookies. If the server time is wrong, the session expires instantly. The user gets logged out. This server is perfect.

37. PHP Worker Lifecycle Management

I looked at the pm.max_requests setting again. I increased it to 1000. This restarts the worker after 1000 requests. It cleans up any tiny memory leaks in the theme code. It is a safety net for long-running servers.

38. Conclusion on Wellness Site Maintenance

Working on health sites requires care. They handle patient data. They need to be online 24/7. This theme is good, but the external API was a trap. By tracing the handles and locks, I fixed the trap.

I am done. The terminal is closed. The site is fast. The server is healthy. I will move to the next task.

39. Final Check on Resource Temporarily Unavailable

The "Resource temporarily unavailable" error is gone. This error is a classic sign of file locks. If you see it, look for flock. Look for lsof. Look for the PID. This is the way of the site administrator.

40. Closing the Technical Note

The server is running Debian 11. It uses PHP 8.1. It uses Nginx 1.18. The theme is ChiroHeal. The site is fast. The audit is complete. I am logging out. (End of report.)

评论 0