You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Introduce DBUtils PooledDB db connection pooling (#3)
* refactored database.db to app.db to simplify project structure
* implement db connection pooling with DBUtils PooledDB (pooled_db)
* minor fix in Handler: only close db connection if it was initialized
* minor fix for previous commit
* start explicit transaction with db.begin() in Cleanup, as required by DBUtils PooledDB
* moved logger and db cleanup code into destructor of Handler
made DBUtils PooledDB params configurable via .env vars
* added db connection pooling configuration notes to README
getting ready for v0.6.0 release
- Improved performance and stability by introducing database connection pooling, using [DBUtils PooledDB (pooled_db)](https://webwareforpython.github.io/DBUtils/main.html#pooleddb-pooled-db)
8
+
- Moved logger and db cleanup code into destructor of `Handler`.
9
+
- Refactored `database.db` to `app.db` to simplify project structure.
10
+
11
+
**Added:**
12
+
13
+
- Added environment variables `DB_POOL_MINCACHED`, `DB_POOL_MAXCACHED`, `DB_POOL_MAXSHARED`, `DB_POOL_MAXUSAGE` for db connection pooling fine-tuning.
14
+
15
+
**Fixed:**
16
+
17
+
- Fix `Lost connection to MySQL server during query ` and ``AttributeError: 'NoneType' object has no attribute 'read'` (on db cursor) connectivity issues by introducing connection pooling.
- Fix `AttributeError: 'NoneType' object has no attribute 'read' in Ratelimit.find()`edge case where `sasl_username` was set in Postfix DATA but empty. We now bail out early if `sasl_username` either does not exist or is empty.
28
+
- Fix edge case where `sasl_username` was set in Postfix DATA but empty. We now bail out early if `sasl_username` either does not exist or is empty.
Copy file name to clipboardExpand all lines: README.md
+22-10Lines changed: 22 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,10 +17,11 @@ Actually, PolicydRateGuard is just a super simple Postfix policy daemon with onl
17
17
But let me name some features that make it stand out from other solutions:
18
18
19
19
-**Super easy Postfix integration** using `check_policy_service` in `smtpd_data_restrictions`
20
-
- Set **individual sender (SASL username) quotas**
20
+
-**Tuned for high performance**, using network or unix sockets, threading, and db connection pooling.
21
+
- Set **individual sender (SASL username) quotas**, which can be both persistent or only for the current time period.
21
22
- Limit senders to **number of recipients** per time period
22
23
- Automatically fills `ratelimits` table with new senders (SASL username) upon first email sent
23
-
- Set your own time period (usually 24hrs) by resetting the counters via Systemd cleanup timer (or cronjob)
24
+
- Set your own **time period (usually 24hrs)** by resetting the counters via Systemd cleanup timer (or cronjob)
24
25
- Continues to raise counters (`msg_counter`, `rcpt_counter`) even in over quota state, so you know if a sender keeps retrying/spamming.
25
26
- Keeps totals of all messages/recipients sent for each sender (SASL username)
26
27
- Stores both **message and recipient counters** in database (`ratelimits` table)
@@ -29,9 +30,10 @@ But let me name some features that make it stand out from other solutions:
29
30
-**Maximum failure safety:** On any unexpected exception, the daemon still replies with a `DUNNO` action, so that the mail is not getting rejected by Postfix. This is done both on Postfix integration side and application exception handling side.
30
31
-**Block action message**`"Rate limit reached, retry later."` can be configured.
31
32
- Lots of configuration params via a simple `.env`
32
-
-**Tuned for high performance**, using network or unix sockets, and threading.
33
33
-**Secure setup**, nothing running under `root`, only on `postfix` user.
34
-
- A super slick minimal codebase with **only a few dependencies** ([PyMySQL](https://pypi.org/project/pymysql/), [python-dotenv](https://pypi.org/project/python-dotenv/), [yoyo-migrations](https://pypi.org/project/yoyo-migrations/)), using Python virtual environment for easy `pip` install. PyMySQL is a pure-Python MySQL client library, so you won't have any trouble on any future major system upgrades.
34
+
- A multi-threaded app that uses [DBUtils PooledDB (pooled_db)](https://github.com/WebwareForPython/DBUtils) for **robust and efficient DB connection handling**.
35
+
- Can be used with any [DB-API 2 (PEP 249)](https://peps.python.org/pep-0249/) conformant database adapter (currently supported: PyMySQL, sqlite3)
36
+
- A super slick minimal codebase with **only a few dependencies** ([PyMySQL](https://pypi.org/project/pymysql/), [DBUtils](https://webwareforpython.github.io/DBUtils/), [python-dotenv](https://pypi.org/project/python-dotenv/), [yoyo-migrations](https://pypi.org/project/yoyo-migrations/)), using Python virtual environment for easy `pip` install. PyMySQL is a pure-Python MySQL client library, so you won't have any trouble on any future major system upgrades.
35
37
- Provides an Ansible Galaxy role [`onlime.policyd_rate_guard`](https://galaxy.ansible.com/onlime/policyd_rate_guard) for easy installation on a Debian mailserver.
36
38
- A **well maintained** project, as it is in active use at [Onlime GmbH](https://www.onlime.ch/), a Swiss webhoster with a rock-solid mailserver architecture.
37
39
@@ -145,11 +147,12 @@ smtpd_data_restrictions =
145
147
permit
146
148
```
147
149
148
-
> **IMPORTANT:** We strongly recommend the advanced policy client configuration (supported since Postfix 3.0), using above syntax with **default action `DUNNO`**, instead of just using `check_policy_service inet:127.0.0.1:10033`.
149
-
>
150
-
> It ensures that if PolicydRateGuard becomes unavailable for any reason, Postfix will ignore it and keep accepting mail as if the rule was not there. PolicydRateGuard should be considered a "non-critical" policy service and you should use some monitoring solution to ensure it is always running as expected.
150
+
**IMPORTANT:** We strongly recommend the advanced policy client configuration (supported since Postfix 3.0), using above syntax with **default action `DUNNO`**, instead of just using `check_policy_service inet:127.0.0.1:10033`.
151
+
152
+
It ensures that if PolicydRateGuard becomes unavailable for any reason, Postfix will ignore it and keep accepting mail as if the rule was not there. PolicydRateGuard should be considered a "non-critical" policy service and you should use some monitoring solution to ensure it is always running as expected.
151
153
152
-
> **NOTE:** You may use `unix:rateguard/policyd` instead of `inet:127.0.0.1:10033` if you have configured PolicydRateGuard to use a unix socket (`SOCKET="/var/spool/postfix/rateguard/policyd"` environment variable).
154
+
> [!NOTE]
155
+
> You may use `unix:rateguard/policyd` instead of `inet:127.0.0.1:10033` if you have configured PolicydRateGuard to use a unix socket (`SOCKET="/var/spool/postfix/rateguard/policyd"` environment variable).
153
156
154
157
Make sure to reload Postfix after this change:
155
158
@@ -225,13 +228,21 @@ PolicydRateGuard can be fully configured through environment variables in `.env`
225
228
-`SENTRY_ENVIRONMENT`
226
229
Sentry environment. Suggested values: `dev` or `prod`, but can be any custom string. Defaults to `dev`.
227
230
231
+
You may also tune the database connection pooling by modifying the following environment variables (defaults are fine for most environments, and you'll find e detailed description in the [DBUtils PooledDB](https://webwareforpython.github.io/DBUtils/main.html#pooleddb-pooled-db-1) usage docs):
232
+
233
+
-`DB_POOL_MINCACHED` (default: `0`)
234
+
-`DB_POOL_MAXCACHED` (default: `10`
235
+
-`DB_POOL_MAXSHARED` (default: `10`)
236
+
-`DB_POOL_MAXUSAGE` (default: `10000`)
237
+
228
238
For production, we recommend to start by copying `.env.example` and then fine-tune your `.env`:
229
239
230
240
```bash
231
241
$ cp .env.example .env
232
242
```
233
243
234
-
> **NOTE:** Minimally, you should set `DB_PASSWORD`, and maybe enable `SYSLOG` logging. For all the other config params it's usually fine to stick with the defaults.
244
+
> [!NOTE]
245
+
> Minimally, you should set `DB_PASSWORD`, and maybe enable `SYSLOG` logging. For all the other config params it's usually fine to stick with the defaults.
235
246
236
247
## Development 👩💻
237
248
@@ -337,6 +348,7 @@ $ . venv/bin/activate
337
348
(venv)$ ./tests.sh
338
349
```
339
350
351
+
> [!IMPORTANT]
340
352
> Make sure to always run the tests inside your venv!
341
353
342
354
### Configure Sentry SDK
@@ -389,8 +401,8 @@ Planned features (coming soon):
389
401
-[x]**Sentry** integration for exception reporting
390
402
-[x]**Ansible role** for easy production deployment
391
403
-[x]**Github workflow** for CI/testing
392
-
-[ ]**Publish package** to [PyPI](https://pypi.org/)
393
404
-[ ] Implement a **configurable webhook API** call for notification to sender on reaching quota limit (on first block) to external service.
405
+
-[ ]**Publish package** to [PyPI](https://pypi.org/) (Might need some restructuring. Any help greatly appreciated!)
0 commit comments