I have a Production and Development bench setup on my server
I started bench using bench start
and accessed my site on localhost:8001
My ssh connection cut, I’m still able to access the site but can not redo bench start
, it says
redis_queue.1 - 127.0.0.1:11001: bind: Address already in use
How do I stop this without disturbing my other bench?
- I can’t CTRL + C
-
fg
command says there’s nothing in the background
-
sudo service supervisor stop
- will only affect my production
- Is my only option so far is hunting down and killing the processes using these ports?
– sudo kill `sudo lsof -t -i:port`
OR something similar How to stop bench - #15 by brian_pond
– This works but it can’t be the best option, I see so many memory leak errors
1 Like
Hi Adam - Yes, your only option is hunting down and killing processes using the ports.
I agree with you, it’s not a great option. But there’s nothing else better.
There should be a bench command that does this. But we need to convince/pay someone to write the code.
1 Like
Too bad.
I’ll give a shot at automating “the hunt” with a python script when I can make time. The ERPNext setup file install.py is a great reference for this.
I’ll update this thread when done.
Ok, I took like 5 minutes to put down this script (edit: much more than 5 minutes by now). It’s far from perfect but will help simplify what is yet to become bench stop.
Setup
- Create stop.py in your development bench directory. e.g sudo nano ~/my-bench/stop.py
- Paste the code below
- Test the script by bench start then break your ssh connection. Login and execute python stop.py in your bench directory.
- Improvement contributions are welcome
The code
""" stop.py is an attempt at creating an easy 'bench stop' command
Expected improvements:
- refactor
- merge with bench
- check for production where stop command will use existing supervisor
- check for non-ubuntu platforms
"""
import os, socket, errno
# Getting port suffix from current redis config.
ports = [1100, 1200, 1300, 900, 800]
lines = {}
port_suffix = 0;
sockets = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with open("./config/redis_cache.conf") as config_file:
for line in config_file:
key, value = line.partition(" ")[::2]
lines[key.strip()] = value.strip()
port_suffix = lines["port"][-1:]
config_file.close()
for port in ports:
port = int("".join([str(port), str(port_suffix)]))
try:
sockets.bind(("127.0.0.1", port))
except socket.error as e:
if e.errno == errno.EADDRINUSE:
os.system("echo 'shutdown' | redis-cli -h 127.0.0.1 -p %d" % port)
print 'Port %d' % port, 'closed'
sockets.close()
print 'bench stopped'
Happy Frappe-ing!
Edit: Another improvement to mitigate this issue has been making my ssh more resilient to disconnections by increasing my **ServerAliveInterval** duration as below.
sudo nano ~/.ssh/config
Host *
ServerAliveInterval 240
ServerAliveCountMax 10
2 Likes
I like it!
I worry a little about immediately going with the kill command. What you do think of this, Adam?
- Try to shutdown Redis gracefully
- echo “shutdown” | redis-cli -h 127.0.0.1 -p 11000
-
SHUTDOWN | Redis : Redis attempts to make a save point.
- After that’s been done (all 3 ports), then execute the kill commands, in case there’s something still running.
1 Like
@brian_pond
Thank for that contribution, its so graceful now. Works like a charm, everytime!
I’ve updated the code above. Next update will probably go to github, with a link back here.
Here goes:
Installation
$ cd frappe-bench #or whatever your bench directory is
$ git clone https://github.com/proenterprise/bench-stop
$ mv ./bench-stop/stop.py .
$ sudo rm -r ./bench-stop
Usage
$ python stop.py
5 Likes
Fantastic. Adding more people to this conversation.
TL;DR - Adam’s code basically does a “bench stop” command. Even when your terminal is closed or missing.
@codingCoffee @saurabh6790 → You are the Bench module maintainers. Any objection to Adam’s code becoming part of Bench repository?
I am tagging other people below, because they previously discussed this topic on the forums. I want them to know you’ve created a solution, Adam.
@pratu16x7
@ManasSolanki
@vrms
@revant_one
@Ahmad_Mardianto
2 Likes
I submitted a pull request for a tiny improvement:
It adds the following as the first line in stop.py:
#!/usr/bin/env python3
If you are in older versions or have a symlink from P3 to P, you’ll prefer:
#!/usr/bin/env python
Also run:
chmod +x stop.py
Having made those two changes, you can simply do…
./stop.py
… instead of …
python stop.py
Having said all that, I actually just have to type …
bnrst
… because I set up a shell alias …
alias bnrst="cd /home/erpdev/frappe-bench/; sudo -A supervisorctl stop all; ./stop.py; bench start;"
… which is enabled automatically on log in because it is stored in the file …
${HOME}/.bash_aliases
Never forget…
The Three Heavenly Sins of Programmers
Sloth: The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful and document what you wrote so you don’t have to answer so many questions about it.
Impatience: The anger you feel when the computer is being lazy. This makes you write programs that don’t just react to your needs, but actually anticipate them. Or at least pretend to.
Hubris: The quality that makes you write (and maintain) programs that other people won’t want to say bad things about.
4 Likes
I have make small update to this code to make bench restart
usage : create file called restart.py and put code in it and run
python3 ~/restart.py
#!/usr/bin/env python
"""
Port Management Utility for Bench
This script manages bench service ports by gracefully shutting them down or forcefully
closing them if necessary. It handles ports for different services (1100x, 1200x, etc.)
where 'x' is determined by the redis_cache.conf configuration.
Usage:
cd ~/bench-directory
python3 stop.py
The script will attempt a graceful port shutdown first, followed by a forceful
closure if needed. Supports both Linux and other Unix-based systems.
"""
import os, socket, errno, time, platform
def get_port_suffix():
"""
Reads the port suffix from redis_cache.conf file.
Returns:
str: Last digit of the port number from config, or '0' if not found
Exits:
If redis_cache.conf file is not found in config directory
"""
try:
with open("./config/redis_cache.conf") as f:
for line in f:
if line.startswith('port'):
return line.split()[1][-1]
except IOError:
print('Error: Ensure this is running from your bench directory, ./config/redis_cache.conf not found.')
exit(1)
return '0'
def stop_port(port, is_linux):
"""
Attempts to stop services running on a specific port.
First tries to bind to the port to check if it's in use.
If port is in use:
1. Attempts graceful port shutdown
2. If still in use, forces port closure using OS-specific commands
Args:
port (int): Port number to stop
is_linux (bool): True if running on Linux, False otherwise
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.bind(("127.0.0.1", port))
except socket.error as e:
if e.errno == errno.EADDRINUSE:
# Try graceful Redis shutdown first, redirect stderr to /dev/null
os.system(f"echo 'shutdown' | redis-cli -h 127.0.0.1 -p {port} 2>/dev/null")
time.sleep(1)
# Force kill if still in use
if is_linux:
os.system(f"fuser {port}/tcp -k")
else:
os.system(f"lsof -i tcp:{port} | grep -v PID | awk '{{print $2}}' | xargs kill")
finally:
sock.close()
# print(f'Closed Port {port}')
def start_bench():
"""
Restarts bench after stopping all ports.
"""
# print('Starting bench.')
os.system('bench start')
def main():
"""
Main execution function.
Determines port suffix and OS type, then attempts to stop services
on all required ports (1100x, 1200x, 1300x, 900x, 800x).
"""
port_suffix = get_port_suffix()
is_linux = 'Linux' in platform.platform()
for base_port in [1100, 1200, 1300, 900, 800]:
port = int(f"{base_port}{port_suffix}")
stop_port(port, is_linux)
# print('bench stopped')
start_bench()
if __name__ == '__main__':
main()
2 Likes