Skip to content

Commit 18f4d50

Browse files
feat: add pool_maxsize parameter to connection managers (#651)
* feat: add pool_maxsize parameter to connection managers Introduces the `pool_maxsize` parameter to `KeycloakOpenID` and `KeycloakAdmin` classes, allowing control over the underlying connection pool size in the `ConnectionManager`. Adds corresponding tests to verify the parameter is correctly passed and stored for both synchronous and asynchronous clients. * feat: apply pool maxsize for async as well --------- Co-authored-by: Richard Nemeth <ryshoooo@gmail.com>
1 parent 7d9b8af commit 18f4d50

8 files changed

Lines changed: 65 additions & 5 deletions

File tree

docs/source/modules/admin.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ Configure admin client
1515
username='example-admin',
1616
password='secret',
1717
realm_name="master",
18-
user_realm_name="only_if_other_realm_than_master")
18+
user_realm_name="only_if_other_realm_than_master",
19+
pool_maxsize=20)
1920
2021
2122
Configure admin client with connection
@@ -34,6 +35,7 @@ Configure admin client with connection
3435
user_realm_name="only_if_other_realm_than_master",
3536
client_id="my_client",
3637
client_secret_key="client-secret",
38+
pool_maxsize=25,
3739
verify=True)
3840
3941
keycloak_admin = KeycloakAdmin(connection=keycloak_connection)

docs/source/modules/openid_client.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ Configure client OpenID
1616
keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/",
1717
client_id="example_client",
1818
realm_name="example_realm",
19-
client_secret_key="secret")
19+
client_secret_key="secret",
20+
pool_maxsize=15) # Example: Set connection pool size
2021
2122
2223
Get .well_know

src/keycloak/connection.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class ConnectionManager:
5959
:type cert: Union[str,Tuple[str,str]]
6060
:param max_retries: The total number of times to retry HTTP requests.
6161
:type max_retries: int
62+
:param pool_maxsize: The maximum number of connections to save in the pool.
63+
:type pool_maxsize: int
6264
"""
6365

6466
def __init__(
@@ -70,6 +72,7 @@ def __init__(
7072
proxies: dict | None = None,
7173
cert: str | tuple | None = None,
7274
max_retries: int = 1,
75+
pool_maxsize: int | None = None,
7376
) -> None:
7477
"""
7578
Init method.
@@ -91,19 +94,25 @@ def __init__(
9194
:type cert: Union[str,Tuple[str,str]]
9295
:param max_retries: The total number of times to retry HTTP requests.
9396
:type max_retries: int
97+
:param pool_maxsize: The maximum number of connections to save in the pool.
98+
:type pool_maxsize: int
9499
"""
95100
self.base_url = base_url
96101
self.headers = headers
97102
self.timeout = timeout
98103
self.verify = verify
99104
self.cert = cert
105+
self.pool_maxsize = pool_maxsize
100106
self._s = requests.Session()
101107
self._s.auth = lambda x: x # don't let requests add auth headers
102108

103109
# retry once to reset connection with Keycloak after tomcat's ConnectionTimeout
104110
# see https://github.com/marcospereirampj/python-keycloak/issues/36
105111
for protocol in ("https://", "http://"):
106-
adapter = HTTPAdapter(max_retries=max_retries)
112+
adapter_kwargs = {"max_retries": max_retries}
113+
if pool_maxsize is not None:
114+
adapter_kwargs["pool_maxsize"] = pool_maxsize
115+
adapter = HTTPAdapter(**adapter_kwargs)
107116
# adds POST to retry whitelist
108117
allowed_methods = set(adapter.max_retries.allowed_methods)
109118
allowed_methods.add("POST")
@@ -114,7 +123,15 @@ def __init__(
114123
if proxies:
115124
self._s.proxies.update(proxies)
116125

117-
self.async_s = httpx.AsyncClient(verify=verify, mounts=proxies, cert=cert)
126+
self.async_s = httpx.AsyncClient(
127+
verify=verify,
128+
mounts=proxies,
129+
cert=cert,
130+
limits=httpx.Limits(
131+
max_connections=100 if pool_maxsize is None else pool_maxsize,
132+
max_keepalive_connections=20,
133+
),
134+
)
118135
self.async_s.auth = None # don't let requests add auth headers
119136
self.async_s.transport = httpx.AsyncHTTPTransport(retries=1)
120137

@@ -184,6 +201,20 @@ def cert(self) -> str | tuple:
184201
def cert(self, value: str | tuple) -> None:
185202
self._cert = value
186203

204+
@property
205+
def pool_maxsize(self) -> int | None:
206+
"""
207+
Return the maximum number of connections to save in the pool.
208+
209+
:returns: Pool maxsize
210+
:rtype: int or None
211+
"""
212+
return self._pool_maxsize
213+
214+
@pool_maxsize.setter
215+
def pool_maxsize(self, value: int | None) -> None:
216+
self._pool_maxsize = value
217+
187218
@property
188219
def headers(self) -> dict:
189220
"""

src/keycloak/keycloak_admin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ class KeycloakAdmin:
8888
:type max_retries: int
8989
:param connection: A KeycloakOpenIDConnection as an alternative to individual params.
9090
:type connection: KeycloakOpenIDConnection
91+
:param pool_maxsize: The maximum number of connections to save in the pool.
92+
:type pool_maxsize: int
9193
"""
9294

9395
PAGE_SIZE = 100
@@ -110,6 +112,7 @@ def __init__(
110112
cert: str | tuple | None = None,
111113
max_retries: int = 1,
112114
connection: KeycloakOpenIDConnection | None = None,
115+
pool_maxsize: int | None = None,
113116
) -> None:
114117
"""
115118
Init method.
@@ -149,6 +152,8 @@ def __init__(
149152
:type max_retries: int
150153
:param connection: An OpenID Connection as an alternative to individual params.
151154
:type connection: KeycloakOpenIDConnection
155+
:param pool_maxsize: The maximum number of connections to save in the pool.
156+
:type pool_maxsize: int
152157
"""
153158
self.connection = connection or KeycloakOpenIDConnection(
154159
server_url=server_url,
@@ -166,6 +171,7 @@ def __init__(
166171
timeout=timeout,
167172
cert=cert,
168173
max_retries=max_retries,
174+
pool_maxsize=pool_maxsize,
169175
)
170176

171177
@property

src/keycloak/keycloak_openid.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ class KeycloakOpenID:
8787
Either a path to an SSL certificate file, or two-tuple of
8888
(certificate file, key file).
8989
:param max_retries: The total number of times to retry HTTP requests.
90-
:type max_retries: int
90+
:param pool_maxsize: The maximum number of connections to save in the pool.
91+
:type pool_maxsize: int
9192
"""
9293

9394
def __init__(
@@ -102,6 +103,7 @@ def __init__(
102103
timeout: int = 60,
103104
cert: str | tuple | None = None,
104105
max_retries: int = 1,
106+
pool_maxsize: int | None = None,
105107
) -> None:
106108
"""
107109
Init method.
@@ -129,6 +131,8 @@ def __init__(
129131
:type cert: Union[str,Tuple[str,str]]
130132
:param max_retries: The total number of times to retry HTTP requests.
131133
:type max_retries: int
134+
:param pool_maxsize: The maximum number of connections to save in the pool.
135+
:type pool_maxsize: int
132136
"""
133137
self.client_id = client_id
134138
self.client_secret_key = client_secret_key
@@ -142,6 +146,7 @@ def __init__(
142146
proxies=proxies,
143147
cert=cert,
144148
max_retries=max_retries,
149+
pool_maxsize=pool_maxsize,
145150
)
146151

147152
self.authorization = Authorization()

src/keycloak/openid_connection.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def __init__(
8282
timeout: int | None = 60,
8383
cert: str | tuple | None = None,
8484
max_retries: int = 1,
85+
pool_maxsize: int | None = None,
8586
) -> None:
8687
"""
8788
Init method.
@@ -120,6 +121,8 @@ def __init__(
120121
:type cert: Union[str,Tuple[str,str]]
121122
:param max_retries: The total number of times to retry HTTP requests.
122123
:type max_retries: int
124+
:param pool_maxsize: The maximum number of connections to save in the pool.
125+
:type pool_maxsize: int
123126
"""
124127
# token is renewed when it hits 90% of its lifetime. This is to account for any possible
125128
# clock skew.
@@ -154,6 +157,7 @@ def __init__(
154157
verify=self.verify,
155158
cert=cert,
156159
max_retries=max_retries,
160+
pool_maxsize=pool_maxsize,
157161
)
158162

159163
@property

tests/test_keycloak_admin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
5757
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
5858
username=env.keycloak_admin,
5959
password=env.keycloak_admin_password,
60+
pool_maxsize=5,
6061
)
6162
assert admin.connection.server_url == f"http://{env.keycloak_host}:{env.keycloak_port}", (
6263
admin.connection.server_url
@@ -72,6 +73,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
7273
assert admin.connection.token is None, admin.connection.token
7374
assert admin.connection.user_realm_name is None, admin.connection.user_realm_name
7475
assert admin.connection.custom_headers is None, admin.connection.custom_headers
76+
assert admin.connection.pool_maxsize == 5, admin.connection.pool_maxsize
7577

7678
admin = KeycloakAdmin(
7779
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",

tests/test_keycloak_openid.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,22 @@ def test_keycloak_openid_init(env: KeycloakTestEnv) -> None:
3535
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
3636
realm_name="master",
3737
client_id="admin-cli",
38+
pool_maxsize=5,
3839
)
3940

4041
assert oid.client_id == "admin-cli"
4142
assert oid.client_secret_key is None
4243
assert oid.realm_name == "master"
4344
assert isinstance(oid.connection, ConnectionManager)
4445
assert isinstance(oid.authorization, Authorization)
46+
assert oid.connection.pool_maxsize == 5
47+
48+
oid_default = KeycloakOpenID(
49+
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
50+
realm_name="master",
51+
client_id="admin-cli",
52+
)
53+
assert oid_default.connection.pool_maxsize is None
4554

4655

4756
def test_well_known(oid: KeycloakOpenID) -> None:

0 commit comments

Comments
 (0)