Skip to content

Commit c6eaace

Browse files
authored
Move pid file write/remove within AcceptorPool (#708)
* Move pid file write/remove within AcceptorPool * Remove unused
1 parent db8da4f commit c6eaace

File tree

5 files changed

+56
-62
lines changed

5 files changed

+56
-62
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,18 +1780,31 @@ ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:997)
17801780

17811781
```console
17821782
+-------------+
1783+
| |
17831784
| Proxy([]) |
1785+
| |
17841786
+------+------+
17851787
|
17861788
|
17871789
+-----------v--------------+
1790+
| |
17881791
| AcceptorPool(...) |
1792+
| |
17891793
+------------+-------------+
17901794
|
1791-
|
17921795
+-----------------+ | +-----------------+
1796+
| | | | |
17931797
| Acceptor(..) <-------------+-----------> Acceptor(..) |
1794-
+-----------------+ +-----------------+
1798+
| | | |
1799+
+---+-------------+ +---------+-------+
1800+
| |
1801+
| |
1802+
| +------++------++------++------++------+ |
1803+
| | || || || || | |
1804+
+----> || || || || <-----+
1805+
| || || || || |
1806+
+------++------++------++------++------+
1807+
Threadless Worker Processes
17951808
```
17961809

17971810
`proxy.py` is made with performance in mind. By default, `proxy.py`

proxy/core/acceptor/pool.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
from ..event import EventQueue
2727

28+
from ...common.utils import bytes_
2829
from ...common.flag import flags
2930
from ...common.constants import DEFAULT_BACKLOG, DEFAULT_IPV6_HOSTNAME
3031
from ...common.constants import DEFAULT_NUM_WORKERS, DEFAULT_PORT
@@ -126,6 +127,7 @@ def __exit__(
126127

127128
def setup(self) -> None:
128129
"""Setup socket and acceptors."""
130+
self._write_pid_file()
129131
if self.flags.unix_socket_path:
130132
self._listen_unix_socket()
131133
else:
@@ -155,6 +157,7 @@ def shutdown(self) -> None:
155157
acceptor.join()
156158
if self.flags.unix_socket_path:
157159
os.remove(self.flags.unix_socket_path)
160+
self._delete_pid_file()
158161
logger.debug('Acceptors shutdown')
159162

160163
def _listen_unix_socket(self) -> None:
@@ -192,3 +195,14 @@ def _start_acceptors(self) -> None:
192195
self.acceptors.append(acceptor)
193196
self.work_queues.append(work_queue[0])
194197
logger.info('Started %d workers' % self.flags.num_workers)
198+
199+
def _write_pid_file(self) -> None:
200+
if self.flags.pid_file is not None:
201+
# NOTE: Multiple instances of proxy.py running on
202+
# same host machine will currently result in overwriting the PID file
203+
with open(self.flags.pid_file, 'wb') as pid_file:
204+
pid_file.write(bytes_(os.getpid()))
205+
206+
def _delete_pid_file(self) -> None:
207+
if self.flags.pid_file and os.path.exists(self.flags.pid_file):
208+
os.remove(self.flags.pid_file)

proxy/proxy.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
:copyright: (c) 2013-present by Abhinav Singh and contributors.
99
:license: BSD, see LICENSE for more details.
1010
"""
11-
import os
1211
import sys
1312
import time
1413
import logging
@@ -18,7 +17,6 @@
1817

1918
from proxy.core.acceptor.work import Work
2019

21-
from .common.utils import bytes_
2220
from .core.acceptor import AcceptorPool
2321
from .http.handler import HttpProtocolHandler
2422
from .core.event import EventManager
@@ -93,15 +91,14 @@
9391

9492

9593
class Proxy:
96-
"""Context manager to control core AcceptorPool server lifecycle.
94+
"""Context manager to control AcceptorPool & Eventing core lifecycle.
9795
98-
By default, AcceptorPool is started with HttpProtocolHandler worker class
99-
i.e. we are only expecting HTTP traffic to flow between clients and server.
96+
By default, AcceptorPool is started with `HttpProtocolHandler` work class.
97+
By definition, it expects HTTP traffic to flow between clients and server.
10098
101-
Optionally, also initialize a global event queue.
102-
It is a multiprocess safe queue which can be used to
103-
build pubsub patterns for message sharing or signaling
104-
within the running proxy environment.
99+
Optionally, it also initializes the eventing core, a multiprocess safe
100+
pubsub system queue which can be used to build various patterns
101+
for message sharing and/or signaling.
105102
"""
106103

107104
def __init__(self, input_args: Optional[List[str]], **opts: Any) -> None:
@@ -113,17 +110,6 @@ def __init__(self, input_args: Optional[List[str]], **opts: Any) -> None:
113110
self.work_klass: Type[Work] = HttpProtocolHandler
114111
self.event_manager: Optional[EventManager] = None
115112

116-
def write_pid_file(self) -> None:
117-
if self.flags.pid_file is not None:
118-
# TODO(abhinavsingh): Multiple instances of proxy.py running on
119-
# same host machine will currently result in overwriting the PID file
120-
with open(self.flags.pid_file, 'wb') as pid_file:
121-
pid_file.write(bytes_(os.getpid()))
122-
123-
def delete_pid_file(self) -> None:
124-
if self.flags.pid_file and os.path.exists(self.flags.pid_file):
125-
os.remove(self.flags.pid_file)
126-
127113
def __enter__(self) -> 'Proxy':
128114
if self.flags.enable_events:
129115
logger.info('Core Event enabled')
@@ -135,7 +121,6 @@ def __enter__(self) -> 'Proxy':
135121
event_queue=self.event_manager.event_queue if self.event_manager is not None else None,
136122
)
137123
self.pool.setup()
138-
self.write_pid_file()
139124
return self
140125

141126
def __exit__(
@@ -149,7 +134,6 @@ def __exit__(
149134
if self.flags.enable_events:
150135
assert self.event_manager is not None
151136
self.event_manager.stop_event_dispatcher()
152-
self.delete_pid_file()
153137

154138

155139
def main(

tests/core/test_acceptor_pool.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@
88
:copyright: (c) 2013-present by Abhinav Singh and contributors.
99
:license: BSD, see LICENSE for more details.
1010
"""
11-
import unittest
11+
import os
1212
import socket
13+
import tempfile
14+
import unittest
15+
1316
from unittest import mock
1417

18+
from proxy.common.utils import bytes_
1519
from proxy.common.flag import FlagParser
1620
from proxy.core.acceptor import AcceptorPool
1721

1822

1923
class TestAcceptorPool(unittest.TestCase):
2024

25+
@mock.patch('os.remove')
26+
@mock.patch('os.path.exists')
27+
@mock.patch('builtins.open')
2128
@mock.patch('proxy.core.acceptor.pool.send_handle')
2229
@mock.patch('multiprocessing.Pipe')
2330
@mock.patch('socket.socket')
@@ -28,15 +35,19 @@ def test_setup_and_shutdown(
2835
mock_socket: mock.Mock,
2936
mock_pipe: mock.Mock,
3037
mock_send_handle: mock.Mock,
38+
mock_open: mock.Mock,
39+
mock_exists: mock.Mock,
40+
mock_remove: mock.Mock,
3141
) -> None:
3242
acceptor1 = mock.MagicMock()
3343
acceptor2 = mock.MagicMock()
3444
mock_acceptor.side_effect = [acceptor1, acceptor2]
3545

3646
num_workers = 2
47+
pid_file = os.path.join(tempfile.gettempdir(), 'pid')
3748
sock = mock_socket.return_value
3849
work_klass = mock.MagicMock()
39-
flags = FlagParser.initialize(num_workers=2)
50+
flags = FlagParser.initialize(num_workers=2, pid_file=pid_file)
4051

4152
pool = AcceptorPool(flags=flags, work_klass=work_klass)
4253
pool.setup()
@@ -66,5 +77,13 @@ def test_setup_and_shutdown(
6677
sock.close.assert_called()
6778

6879
pool.shutdown()
80+
81+
mock_open.assert_called_with(pid_file, 'wb')
82+
mock_open.return_value.__enter__.return_value.write.assert_called_with(
83+
bytes_(os.getpid()),
84+
)
85+
mock_exists.assert_called_with(pid_file)
86+
mock_remove.assert_called_with(pid_file)
87+
6988
acceptor1.join.assert_called()
7089
acceptor2.join.assert_called()

tests/test_main.py

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
:license: BSD, see LICENSE for more details.
1010
"""
1111
import unittest
12-
import tempfile
13-
import os
1412

1513
from unittest import mock
1614

@@ -208,40 +206,6 @@ def test_enable_devtools(
208206
# Currently --enable-devtools alone doesn't enable eventing core
209207
mock_event_manager.assert_not_called()
210208

211-
@mock.patch('time.sleep')
212-
@mock.patch('os.remove')
213-
@mock.patch('os.path.exists')
214-
@mock.patch('builtins.open')
215-
@mock.patch('proxy.proxy.EventManager')
216-
@mock.patch('proxy.proxy.AcceptorPool')
217-
@mock.patch('proxy.common.flag.FlagParser.parse_args')
218-
def test_pid_file_is_written_and_removed(
219-
self,
220-
mock_parse_args: mock.Mock,
221-
mock_acceptor_pool: mock.Mock,
222-
mock_event_manager: mock.Mock,
223-
mock_open: mock.Mock,
224-
mock_exists: mock.Mock,
225-
mock_remove: mock.Mock,
226-
mock_sleep: mock.Mock,
227-
) -> None:
228-
pid_file = os.path.join(tempfile.gettempdir(), 'pid')
229-
mock_sleep.side_effect = KeyboardInterrupt()
230-
mock_args = mock_parse_args.return_value
231-
self.mock_default_args(mock_args)
232-
mock_args.pid_file = pid_file
233-
main(['--pid-file', pid_file])
234-
mock_parse_args.assert_called_once()
235-
mock_acceptor_pool.assert_called()
236-
mock_acceptor_pool.return_value.setup.assert_called()
237-
mock_event_manager.assert_not_called()
238-
mock_open.assert_called_with(pid_file, 'wb')
239-
mock_open.return_value.__enter__.return_value.write.assert_called_with(
240-
bytes_(os.getpid()),
241-
)
242-
mock_exists.assert_called_with(pid_file)
243-
mock_remove.assert_called_with(pid_file)
244-
245209
@mock.patch('time.sleep')
246210
@mock.patch('proxy.proxy.EventManager')
247211
@mock.patch('proxy.proxy.AcceptorPool')

0 commit comments

Comments
 (0)