High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.
Find a file
Kailash Nadh cea65c009d Fix and refactor subscriber batch fetching in campaign processing.
This has been a hair-pulling rabbit hole of an issue. #1931 and others.
When the `next-campaign-subscribers` query that fetches $n subscribers
per batch for a campaign returns no results, the manager assumes
that the campaign is done and marks as finished.

Marathon debugging revealed fundamental flaws in qyery's logic that
would incorrectly return 0 rows under certain conditions.
- Based on the "layout" of subscribers for eg: a series of blocklisted
  subscribers between confirmed subscribers.
  A series of unconfirmed subscribers in a batch belonging to a double
  opt-in list.
- Bulk import blocklisting users, but not marking their subscriptions
  as 'unsubscribed'.
- Conditions spread across multiple CTEs resulted in returning an
  arbitrary number of rows and $N per batch as the selected $N rows
  would get filtered out elsewhere, possibly even becoming 0.

After fixing this and testing it on our prod instance that has
15 million subscribers and ~70 million subscriptions in the
`subscriber_lists` table, ended up discovered significant inefficiences
in Postgres query planning. When `subscriber_lists` and campaign list IDs
are joined dynamically (CTE or ANY() or any kind of JOIN that involves)
a query, the Postgres query planner is unable to use the right indexes.

After testing dozens of approaches, discovered that statically passing
the values to join on (hardcoding or passing via parametrized $1 vars),
the query uses the right indexes. The difference is staggering.
For the particular scenario on our large prod DB to pull a batch,
~15 seconds vs. ~50ms, a whopping 300x improvement!

This patch splits `next-campaign-subscribers` into two separate queries,
one which fetches campaign metadata and list_ids, whose values are then
passed statically to the next query to fetch subscribers by batch.

In addition, it fixes and refactors broken filtering and counting logic
in `create-campaign` and `next-campaign` queries.

Closes #1931, #1993, #1986.
2024-10-13 17:03:59 +05:30
.github Add issue/PR comment messages to GitHub stale action bot. 2024-07-17 11:46:55 +05:30
cmd Fix and refactor subscriber batch fetching in campaign processing. 2024-10-13 17:03:59 +05:30
dev Provide a default configuration file for containerized development 2022-01-19 09:30:23 -05:00
docs Add docs for v4.x.x multi-user upgrade changes. 2024-10-13 16:59:52 +05:30
frontend Update profile UI with new user data structures. 2024-10-13 17:03:58 +05:30
i18n Add support for "list roles". 2024-10-13 17:03:58 +05:30
internal Fix and refactor subscriber batch fetching in campaign processing. 2024-10-13 17:03:59 +05:30
models Fix and refactor subscriber batch fetching in campaign processing. 2024-10-13 17:03:59 +05:30
scripts Add script to merge and normalize i18n files 2021-04-14 13:52:13 +05:30
static Update OIDC auth URL in login form. 2024-10-13 16:59:52 +05:30
.dockerignore
.gitattributes Added end of line config for git 2022-01-26 09:50:06 +01:00
.gitignore feat: Add separate config for demo setup, tweak docs 2020-07-08 22:38:31 +05:30
.goreleaser.yml Add support for running Docker container as non-root user using docker-entrypoint.sh (#1892) 2024-07-21 11:03:15 +05:30
config-demo.toml Don't indent TOML keys deeper than their sections 2021-05-06 18:28:04 +03:00
config.toml.sample Remove admin user/password from sample config generation. 2024-10-13 16:59:52 +05:30
CONTRIBUTING.md Fix link to the docs repo. 2023-03-26 11:17:15 +05:30
docker-compose.yml Refactor handler groups and add mising auth features like logout. 2024-10-13 16:59:51 +05:30
docker-entrypoint.sh Add support for running Docker container as non-root user using docker-entrypoint.sh (#1892) 2024-07-21 11:03:15 +05:30
Dockerfile Add support for running Docker container as non-root user using docker-entrypoint.sh (#1892) 2024-07-21 11:03:15 +05:30
go.mod Upgrade simplesessions to v3. 2024-10-13 16:59:51 +05:30
go.sum Upgrade simplesessions to v3. 2024-10-13 16:59:51 +05:30
install-demo.sh fix: easy install docker script for macOS (#1742) 2024-02-21 13:11:38 +05:30
install-prod.sh Slightly improve docker-compose feedback (#2054) 2024-10-04 10:28:22 +05:30
LICENSE
listmonk-simple.service Update listmonk-simple.service - add optional log file (#1640) 2024-01-09 23:41:14 +05:30
listmonk@.service Enable extra system calls in systemd service (#1309) 2023-07-12 19:42:54 +05:30
Makefile Refactor 'super' user type to a pre-defined super admin role. 2024-10-13 16:59:52 +05:30
permissions.json Add list permission check to subscriber calls. 2024-10-13 17:03:55 +05:30
project.inlang.json fix: update inlang settings (#1529) 2023-09-20 14:19:08 +05:30
queries.sql Fix and refactor subscriber batch fetching in campaign processing. 2024-10-13 17:03:59 +05:30
README.md Update README.md (#2034) 2024-09-06 15:49:53 +05:30
schema.sql Fix and refactor subscriber batch fetching in campaign processing. 2024-10-13 17:03:59 +05:30
VERSION Add a VERSION file for git-archive export 2021-08-14 13:41:19 +05:30

listmonk-logo

listmonk is a standalone, self-hosted, newsletter and mailing list manager. It is fast, feature-rich, and packed into a single binary. It uses a PostgreSQL (⩾ 12) database as its data store.

listmonk-dashboard

Visit listmonk.app for more info. Check out the live demo.

Installation

Docker

The latest image is available on DockerHub at listmonk/listmonk:latest. Use the sample docker-compose.yml to run manually or use the helper script.

Demo

mkdir listmonk-demo && cd listmonk-demo
bash -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)"

DO NOT use this demo setup in production.

Production

mkdir listmonk && cd listmonk
bash -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)"

Visit http://localhost:9000.

NOTE: Always examine the contents of shell scripts before executing them.

See installation docs.


Binary

  • Download the latest release and extract the listmonk binary.
  • ./listmonk --new-config to generate config.toml. Then, edit the file.
  • ./listmonk --install to setup the Postgres DB (or --upgrade to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects).
  • Run ./listmonk and visit http://localhost:9000.

See installation docs.


Developers

listmonk is free and open source software licensed under AGPLv3. If you are interested in contributing, refer to the developer setup. The backend is written in Go and the frontend is Vue with Buefy for UI.

License

listmonk is licensed under the AGPL v3 license.