Skip to content
This repository was archived by the owner on Mar 5, 2020. It is now read-only.

Commit 3647898

Browse files
committed
Add configurable memory options to IPC layer.
1 parent 7f3b80a commit 3647898

File tree

2 files changed

+88
-12
lines changed

2 files changed

+88
-12
lines changed

README.rst

+73-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,82 @@ Usage
1919

2020
You'll need to instantiate the channel layer with a path prefix to create
2121
IPC objects underneath; any channel layers with the same prefix will talk to
22-
each other.
23-
24-
* ``prefix``: Prefix to use for IPC objects in the root namespace. Defaults to ``asgi``.
22+
each other as long as they're on the same machine.
2523

2624
Example::
2725

2826
channel_layer = IPCChannelLayer(
2927
prefix="aeracode",
28+
channel_memory=200 * 1024 * 1024,
3029
)
30+
31+
prefix
32+
~~~~~~
33+
34+
Prefix to use for IPC objects under the root namespace. Defaults to ``asgi``.
35+
IPC layers on the same machine with the same prefix will talk to each other.
36+
37+
channel_memory
38+
~~~~~~~~~~~~~~
39+
40+
The amount of shared memory to allocate to the channel storage, in bytes.
41+
Defaults to 100MB. All of your in-flight messages must fit into this,
42+
otherwise you'll get ``ChannelFull`` errors if the memory space is full up.
43+
44+
ASGI messages can be a maximum of one megabyte, and are usually much smaller.
45+
The IPC routing metadata on top of each message is approximately 50 bytes.
46+
47+
group_memory
48+
~~~~~~~~~~~~
49+
50+
The amount of shared memory to allocate to the group storage, in bytes.
51+
Defaults to 20MB. All of your group membership data must fit into this space,
52+
otherwise your group memberships may fail to persist.
53+
54+
You can fit approximately 4000 group-channel membership associations into one
55+
megabyte of memory.
56+
57+
expiry
58+
~~~~~~
59+
60+
Message expiry in seconds. Defaults to ``60``. You generally shouldn't need
61+
to change this, but you may want to turn it down if you have peaky traffic you
62+
wish to drop, or up if you have peaky traffic you want to backlog until you
63+
get to it.
64+
65+
group_expiry
66+
~~~~~~~~~~~~
67+
68+
Group expiry in seconds. Defaults to ``86400``. Interface servers will drop
69+
connections after this amount of time; it's recommended you reduce it for a
70+
healthier system that encourages disconnections.
71+
72+
capacity
73+
~~~~~~~~
74+
75+
Default channel capacity. Defaults to ``100``. Once a channel is at capacity,
76+
it will refuse more messages. How this affects different parts of the system
77+
varies; a HTTP server will refuse connections, for example, while Django
78+
sending a response will just wait until there's space.
79+
80+
channel_capacity
81+
~~~~~~~~~~~~~~~~
82+
83+
Per-channel capacity configuration. This lets you tweak the channel capacity
84+
based on the channel name, and supports both globbing and regular expressions.
85+
86+
It should be a dict mapping channel name pattern to desired capacity; if the
87+
dict key is a string, it's intepreted as a glob, while if it's a compiled
88+
``re`` object, it's treated as a regular expression.
89+
90+
This example sets ``http.request`` to 200, all ``http.response!`` channels
91+
to 10, and all ``websocket.send!`` channels to 20::
92+
93+
channel_capacity={
94+
"http.request": 200,
95+
"http.response!*": 10,
96+
re.compile(r"^websocket.send\!.+"): 20,
97+
}
98+
99+
If you want to enforce a matching order, use an ``OrderedDict`` as the
100+
argument; channels will then be matched in the order the dict provides them.

asgi_ipc.py

+15-9
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class IPCChannelLayer(BaseChannelLayer):
3131
little... heavier than that.
3232
"""
3333

34-
def __init__(self, prefix="asgi", expiry=60, group_expiry=86400, capacity=10, channel_capacity=None):
34+
def __init__(self, prefix="asgi", expiry=60, group_expiry=86400, capacity=10, channel_capacity=None, channel_memory=100*MB, group_memory=20*MB):
3535
super(IPCChannelLayer, self).__init__(
3636
expiry=expiry,
3737
group_expiry=group_expiry,
@@ -40,9 +40,9 @@ def __init__(self, prefix="asgi", expiry=60, group_expiry=86400, capacity=10, ch
4040
)
4141
self.thread_lock = threading.Lock()
4242
self.prefix = prefix
43-
self.channel_store = MemoryDict("/%s-channel-dict" % self.prefix, size=100 * MB)
43+
self.channel_store = MemoryDict("/%s-channel-dict" % self.prefix, size=channel_memory)
4444
# Set containing all groups to flush
45-
self.group_store = MemoryDict("/%s-group-dict" % self.prefix, size=20 * MB)
45+
self.group_store = MemoryDict("/%s-group-dict" % self.prefix, size=group_memory)
4646

4747
### ASGI API ###
4848

@@ -193,12 +193,18 @@ def __init__(self, path, size=None):
193193
mode=0o660,
194194
initial_value=1,
195195
)
196-
self.shm = posix_ipc.SharedMemory(
197-
self.path,
198-
flags=posix_ipc.O_CREAT,
199-
mode=0o660,
200-
size=self.size,
201-
)
196+
try:
197+
self.shm = posix_ipc.SharedMemory(
198+
self.path,
199+
flags=posix_ipc.O_CREAT,
200+
mode=0o660,
201+
size=self.size,
202+
)
203+
except ValueError as e:
204+
raise ValueError(
205+
"Unable to allocate shared memory segment (potentially out of memory).\n" +
206+
"Error was: %s" % e
207+
)
202208
self.mmap = mmap.mmap(self.shm.fd, self.size)
203209

204210
@classmethod

0 commit comments

Comments
 (0)