Skip to content

Commit 06f33de

Browse files
committed
Add examples for episode 579 & 580
1 parent e1e5869 commit 06f33de

File tree

9 files changed

+393
-0
lines changed

9 files changed

+393
-0
lines changed

sample_code/ep579/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# [oops I'm the pyuwsgi maintainer now (intermediate)](https://youtu.be/WILYaDNez4g)
2+
3+
today I take you down a rabbit hole of deadlocks and worker reloads and python 3.12 sadness. unfortunately despite the "conclusion" in this video this doesn't actually fix the problem -- more on that in the future!
4+
5+
## Interactive examples
6+
7+
### Python
8+
9+
```python
10+
import pyuwsgi
11+
```
12+
13+
### Bash
14+
15+
Session 1:
16+
17+
```bash
18+
virtualenv venv -ppython3.11
19+
. venv/bin/activate
20+
pip install pyuwsgi
21+
22+
ls venv/lib/python3.11/site-packages/
23+
python
24+
25+
chmod +x t.sh
26+
bash t.sh
27+
kill -9 %1
28+
29+
pip freeze | grep pyuwsgi
30+
deactivate
31+
32+
virtualenv venv -ppython3.12
33+
. venv/bin/activate
34+
pip install pyuwsgi
35+
36+
bash t.sh
37+
```
38+
39+
Session 2:
40+
41+
```bash
42+
yes | head -100 | xargs -P4 --replace curl localhost:9001/health-check/; echo
43+
yes | head -1000 | xargs -P4 --replace curl localhost:9001/health-check/; echo
44+
yes | head -1000 | xargs -P40 --replace curl localhost:9001/health-check/; echo
45+
```

sample_code/ep579/rev01/t.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
UWSGI_AUTO_PROCNAME=true \
2+
UWSGI_BINARY_PATH=$PWD/venv/bin/python3 \
3+
UWSGI_BUFFER_SIZE=32768 \
4+
UWSGI_DIE_ON_TERM=true \
5+
UWSGI_DISABLE_LOGGING=false \
6+
UWSGI_DISABLE_WRITE_EXCEPTION=true \
7+
UWSGI_ENABLE_THREADS=true \
8+
UWSGI_HARAKIRI=600 \
9+
UWSGI_HONOUR_STDIN=true \
10+
UWSGI_HTTP_CHUNKED_INPUT=true \
11+
UWSGI_HTTP_KEEPALIVE=true \
12+
UWSGI_HTTP_SOCKET=127.0.0.1:9001 \
13+
UWSGI_HTTP_TIMEOUT=600 \
14+
UWSGI_IGNORE_SIGPIPE=true \
15+
UWSGI_IGNORE_WRITE_ERRORS=true \
16+
UWSGI_LAZY_APPS=true \
17+
UWSGI_LIMIT_POST=1073741824 \
18+
UWSGI_LOG_X_FORWARDED_FOR=false \
19+
UWSGI_MASTER=true \
20+
UWSGI_MAX_REQUESTS=20 \
21+
UWSGI_MEMORY_REPORT=true \
22+
UWSGI_MODULE=wsgi:application \
23+
UWSGI_NEED_APP=true \
24+
UWSGI_POST_BUFFERING=65536 \
25+
UWSGI_PROCNAME_PREFIX_SPACED=[Sentry] \
26+
UWSGI_PROTOCOL=http \
27+
UWSGI_RELOAD_ON_RSS=600 \
28+
UWSGI_SINGLE_INTERPRETER=true \
29+
UWSGI_THUNDER_LOCK=false \
30+
UWSGI_VACUUM=true \
31+
UWSGI_VIRTUALENV=$PWD/venv \
32+
UWSGI_WORKERS=2 \
33+
UWSGI_THREADS=2 \
34+
UWSGI_WSGI_FILE=wsgi.py \
35+
uwsgi

sample_code/ep579/rev01/wsgi.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def application(environ, start_response):
2+
status = '200 OK'
3+
output = b'!'
4+
5+
response_headers = [
6+
('Content-Type', 'text/plain'),
7+
('Content-Length', str(len(output))),
8+
]
9+
start_response(status, response_headers)
10+
11+
return [output]

sample_code/ep579/rev02/t.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
UWSGI_AUTO_PROCNAME=true \
2+
UWSGI_BINARY_PATH=$PWD/venv/bin/python3 \
3+
UWSGI_BUFFER_SIZE=32768 \
4+
UWSGI_DIE_ON_TERM=true \
5+
UWSGI_DISABLE_LOGGING=false \
6+
UWSGI_DISABLE_WRITE_EXCEPTION=true \
7+
UWSGI_ENABLE_THREADS=true \
8+
UWSGI_HARAKIRI=600 \
9+
UWSGI_HONOUR_STDIN=true \
10+
UWSGI_HTTP_CHUNKED_INPUT=true \
11+
UWSGI_HTTP_KEEPALIVE=true \
12+
UWSGI_HTTP_SOCKET=127.0.0.1:9001 \
13+
UWSGI_HTTP_TIMEOUT=600 \
14+
UWSGI_IGNORE_SIGPIPE=true \
15+
UWSGI_IGNORE_WRITE_ERRORS=true \
16+
UWSGI_LAZY_APPS=true \
17+
UWSGI_LIMIT_POST=1073741824 \
18+
UWSGI_LOG_X_FORWARDED_FOR=false \
19+
UWSGI_MASTER=true \
20+
UWSGI_MAX_REQUESTS=20 \
21+
UWSGI_MEMORY_REPORT=true \
22+
UWSGI_MODULE=wsgi:application \
23+
UWSGI_NEED_APP=true \
24+
UWSGI_POST_BUFFERING=65536 \
25+
UWSGI_PROCNAME_PREFIX_SPACED=[Sentry] \
26+
UWSGI_PROTOCOL=http \
27+
UWSGI_RELOAD_ON_RSS=600 \
28+
UWSGI_SINGLE_INTERPRETER=true \
29+
UWSGI_THUNDER_LOCK=false \
30+
UWSGI_VACUUM=true \
31+
UWSGI_VIRTUALENV=$PWD/venv \
32+
UWSGI_WORKERS=2 \
33+
UWSGI_WSGI_FILE=wsgi.py \
34+
uwsgi

sample_code/ep580/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# [debugging and fixing pyuwsgi in python 3.12 (advanced)](https://youtu.be/Y4n2xCIF2Jg)
2+
3+
welcome to a whirlwind tour of bisecting, GIL, strace, gdb, forking, threadstates, undefined behaviour, and deadlocking. I go through the process debugging the most difficult bug I've encountered yet and ultimately how I fixed the problem and understood what changed and why it resulted in this issue!
4+
5+
## Interactive examples
6+
7+
Session 1:
8+
9+
### Bash
10+
11+
```bash
12+
virtualenv venv -ppython3.12
13+
. venv/bin/activate
14+
pip install pyuwsgi==2.0.26
15+
16+
chmod +x t.sh
17+
bash t.sh
18+
pkill -9 -f uwsgi
19+
fg
20+
21+
cd cpython
22+
git bisect start
23+
git bisect good v3.12.0a2
24+
git bisect bad v3.12.0rc1
25+
git bisect run ../venv-bisect/bin/python3 ../bisect-uwsgi.py
26+
27+
git clone git@github.com:lincolnloop/pyuwsgi-wheels
28+
cd pyuwsgi-wheels/uwsgi/
29+
30+
git grep IsInit
31+
nano plugins/python/python_plugin.c
32+
nano plugins/pyuwsgi/pyuwsgi.c
33+
34+
git grep PyEval_RestoreThread
35+
git grep -n PyEval_RestoreThread
36+
37+
git grep uwsgi_fork
38+
nano core/master_utils.c
39+
40+
./venv.uwsgi/bin/pip uninstall uwsgi
41+
./venv.uwsgi/bin/pip install ./uwsgi
42+
43+
chmod +x t.sh
44+
bash t.sh
45+
46+
git -C ../../uwsgi diff -- core/
47+
git -C ../../uwsgi diff -- core/ | git apply
48+
49+
./venv/bin/pip uninstall pyuwsgi
50+
./venv/bin/pip install ./pyuwsgi-wheels/uwsgi
51+
52+
nano pyuwsgi-wheels/uwsgi/plugins/pyuwsgi/pyuwsgi.c
53+
```
54+
55+
Session 2:
56+
57+
```bash
58+
yes | head -60 | xargs --replace curl localhost:9001/health-check/; echo
59+
```
60+
61+
Session 3:
62+
63+
```bash
64+
which strace
65+
strace --help
66+
67+
ps -ef | grep uwsgi
68+
sudo strace -p <pid>
69+
sudo gdb -p <pid>
70+
71+
# where
72+
# bt
73+
# q
74+
75+
ps -ef | grep uwsgi
76+
sudo gdb -p <pid>
77+
78+
# b plugins/python/gil.c:12
79+
# c
80+
# p _Py_tss_tstate
81+
# c
82+
# q
83+
84+
sudo gdb -p <pid>
85+
86+
# b plugins/python/gil.c:12
87+
# c
88+
# p_Py_tss_tstate->interp->ceval->gil->locked
89+
# p _Py_tss_tstate
90+
# q
91+
92+
sudo gdb -p <pid>
93+
94+
# b plugins/python/gil.c:12
95+
# c
96+
# p _PyRuntime.ceval.gil->locked
97+
# p _Py_tss_tstate
98+
# q
99+
```
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
from __future__ import annotations
2+
import contextlib
3+
import os.path
4+
# import shlex
5+
import subprocess
6+
import urllib.request
7+
import sys
8+
import time
9+
from typing import Generator
10+
# from typing import IO
11+
import psutil
12+
13+
14+
rev = subprocess.check_output(('git', 'rev-parse', 'HEAD'), text=True).strip()
15+
16+
prefix = os.path.abspath('../prefix')
17+
venv = os.path.abspath('../venv')
18+
logs = os.path.abspath('../logs')
19+
uwsgi_src = os.path.abspath('../pyuwsgi-wheels/uwsgi')
20+
21+
22+
def tee() -> None:
23+
...
24+
25+
26+
def _cmd_q() -> None:
27+
...
28+
29+
30+
def _build_cpython() -> None:
31+
print('!!! building cpython')
32+
_cmd_q('git', 'clean', '-fxfd')
33+
_cmd_q('./configure', '--prefix', prefix)
34+
_cmd_q('make', '-j5')
35+
_cmd_q('make', 'install')
36+
37+
38+
def _make_venv() -> None:
39+
print('!!! making venv')
40+
_cmd_q('rm', '-rf', venv)
41+
_cmd_q(os.path.join(prefix, 'bin', 'python3'), '-mvenv', venv)
42+
43+
44+
def _install_uwsgi() -> None:
45+
print('!!! installing uwsgi')
46+
_cmd_q(os.path.join(venv, 'bin', 'pip'), 'install', uwsgi_src)
47+
48+
49+
def _req() -> None:
50+
urllib.request.urlopen('http://127.0.0.1:9001/', timeout=1).read()
51+
52+
53+
def _wait_for_ready() -> None:
54+
for _ in range(10):
55+
try:
56+
_req()
57+
except OSError:
58+
print('...waiting for start', file=sys.stderr)
59+
time.sleep(.25)
60+
else:
61+
return
62+
63+
64+
def _child_pids(pid: int) -> tuple[int, ...]:
65+
return tuple(child.pid for child in psutil.Process(pid).children())
66+
67+
68+
@contextlib.contextmanager
69+
def uwsgi_proc() -> Generator[int]:
70+
uwsgi = os.path.join(venv, 'bin', 'uwsgi')
71+
_cmd_q(uwsgi, '--help')
72+
env = {
73+
**os.environ,
74+
'UWSGI_MASTER': 'true',
75+
'UWSGI_BINARY_PATH': os.path.join(venv, 'bin', 'python3'),
76+
'UWSGI_VIRTUALENV': venv,
77+
'UWSGI_WORKERS': '2',
78+
'UWSGI_ENABLE_THREADS': 'true',
79+
'UWSGI_MAX_REQUESTS': '2',
80+
'UWSGI_SINGLE_INTERPRETER': 'true',
81+
'UWSGI_HTTP_SOCKET': 'localhost:9001',
82+
'UWSGI_WSGI_FILE': 'wsgi.py',
83+
'UWSGI_DISABLE_LOGGING': '1',
84+
}
85+
with open(os.path.join(logs, rev), 'a+') as logfile:
86+
tee('+ uwsgi', logfile, sys.stderr)
87+
proc = subprocess.Popen(
88+
(uwsgi, ),
89+
env=env,
90+
cwd='..',
91+
stdout=logfile,
92+
stderr=logfile,
93+
)
94+
_wait_for_ready()
95+
try:
96+
yield proc.pid
97+
finally:
98+
to_kill = [str(proc.pid)]
99+
for child_pid in _child_pids(proc.pid):
100+
to_kill.append(str(child_pid))
101+
subprocess.call(('kill', '-9', *to_kill))
102+
103+
104+
def _run_test() -> int:
105+
with uwsgi_proc() as pid:
106+
children = set(_child_pids(pid))
107+
print(f'started with pid: {pid} {children}!', file=sys.stderr)
108+
while True:
109+
time.sleep(.25)
110+
try:
111+
_req()
112+
except OSError:
113+
cand_children = set(_child_pids(pid))
114+
assert not children & cand_children, (children, cand_children)
115+
print('\ngot deadlock!!')
116+
return 1
117+
else:
118+
cand_children = set(_child_pids(pid))
119+
if cand_children & children:
120+
print('z', flush=True, end='')
121+
continue
122+
else:
123+
print('\nsuccessful request after all children recycled!')
124+
return 0
125+
126+
127+
def main() -> int:
128+
assert os.path.exists('Python'), 'not in cpython'
129+
_build_cpython()
130+
_make_venv()
131+
_install_uwsgi()
132+
return _run_test()
133+
134+
135+
if __name__ == '__main__':
136+
raise SystemExit(main())

sample_code/ep580/rev01/t.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
UWSGI_MASTER=true \
2+
UWSGI_BINARY_PATH=$PWD/venv/bin/python3 \
3+
UWSGI_VIRTUALENV=$PWD/venv \
4+
UWSGI_WORKERS=2 \
5+
UWSGI_ENABLE_THREADS=true \
6+
UWSGI_MAX_REQUESTS=5 \
7+
UWSGI_HTTP_SOCKET=localhost:9001 \
8+
UWSGI_WSGI_FILE=wsgi.py \
9+
UWSGI_DISABLE_LOGGING=1 \
10+
venv/bin/uwsgi

sample_code/ep580/rev01/wsgi.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def application(environ, start_response):
2+
print('.', end='', flush=True)
3+
4+
status = '200 OK'
5+
output = b'!'
6+
7+
response_headers = [('Content-type', 'text/plain'),
8+
('Content-Length', str(len(output)))]
9+
start_response(status, response_headers)
10+
11+
return [output]

0 commit comments

Comments
 (0)