# Optimised storage for email statistics / view in browser tokens / open details

**URL:** https://forum.mautic.org/t/optimised-storage-for-email-statistics-view-in-browser-tokens-open-details/37273
**Category:** Ideas and Feature Requests
**Tags:** discussion, development, email
**Created:** [February 2, 2026, 9:07am UTC](https://forum.mautic.org/t/optimised-storage-for-email-statistics-view-in-browser-tokens-open-details/37273 "2026-02-02T09:07:55Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![driskell](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.mautic.org/driskell/32/10405_2.png) [@driskell](https://forum.mautic.org/u/driskell)
#### Post date: [February 2, 2026, 9:07am UTC](https://forum.mautic.org/t/optimised-storage-for-email-statistics-view-in-browser-tokens-open-details/37273/1 "2026-02-02T09:07:55Z")

</div>

My idea is:

I’ve been running a patch to maintain the email\_stats growth a bit better and wonder if this is something that might help others so wanted to get some feedback and any further idea.

> <https://github.com/mautic/mautic/pull/15605>
>
> | Q | A
> | --------------------------------…------ | ---
> | Bug fix? (use the a.b branch) | ❌
> | New feature/enhancement? (use the a.x branch) | ✔️
> | Deprecations? | ❌
> | BC breaks? (use the c.x branch) | ❌
> | Automated tests included? | ✔️
> 
> \## Description
> 
> I've been investigating \`email\_stats\` usage and growth for some time and there's a few issues that cause it to bloat massively:
> \* \`tokens\` contains data that is readily available elsewhere, such as contact fields
> \* \`tokens\` contains data that is easy to calculate on-demand, such as tracking IDs
> \* \`tokens\` contains unnecessary entries such as an empty dynamic placeholders item
> \* \`open\_details\` contains a full User-Agent string for every open of an email by a lead
> \* Entries are never removed, even if associated leads are removed
> 
> \`tokens\` contributes to the significant majority of the table size. Fixing those issues is another task. But a cleanup would go a long way to keeping size under control.
> 
> The most common method of reducing the table size in the forums is to cleanup the \`tokens\` field data.
> 
> This PR implements that as a cleanup process called "compaction". It also implements the missing cleanup of entries. The open counts against the emails are incremented separately so I believe the cleanup after 7 years during \`maintenance:cleanup --days-old=2555\` would only impact reports for data going back longer than that. During this same maintenance period compaction takes place. \`tokens\` seems only ever used by "View in web browser" which should not be something a user needs to do on an email sent weeks ago. \`open\_details\` is used to show the user agent details for an open event, I think (couldn't quite find) in audit log for a lead - which again shouldn't be needed years later.
> 
> The default for compaction of \`open\_details\` and \`tokens\` is aggressive - and warrants discussion during review (180 days)! But it is configurable.
> 
> There is a huge risk to deployment here in that the alteration of \`email\_stats\` is indeed a large task - but I couldn't see any other way to do a fast compact without a column indicator. Indexing \`tokens\` and \`open\_details\` would just create massive indexes. So likely might need a warning? Unless there's a better approach?
> 
> Also, \*\*a table rebuild is likely still needed\*\* after this on a periodic basis to release disk space but at least it's a case of user's just running that task infrequently, and not needing to worry about running long-running queries against the database to reduce the data. They can just look at \`Data\_free\` and schedule that in. A future improvement could be to shift \`tokens\` and \`open\_details\` to their own table though - as then old compaction is just deletion which remove entire rows and the table space pages can then be reused effectively unlike with this current cleaning. One for another task, maybe alongside optimising \`tokens.\`
> 
> \### 📋 Steps to test this PR:
> 
> 1. Open this PR on Gitpod or pull down for testing locally (see docs on testing PRs \[here\](https://contribute.mautic.org/contributing-to-mautic/tester))
> 2. Configure compaction threshold to 1 day
> 3. Send some emails and wait a day
> 4. Send some more and wait a day
> 5. Send some more
> 6. Run \`maintenance:cleanup --days-old=2\`
> 7. Ensure email\_stats is reduced to remove stats for the first email
> 8. Ensure tokens and open details are empty for emails on the first and second email
> 
> (Might need tweak timings, or fake them yourself, as days-old might be inclusive)

Previously, email\_stats contained all the following data for each email send to each user:

- The statistics for the send, so recording it was sent, and recording number of opens
- The tokens for viewing in the browser, which is very large amount of data
- The user agents for all opens

This meant you could send up 100s of GBs after many many sends and before you get anywhere near the retention period of the emails you are sending (could be years).

The idea in the PR is to split this out to independent tables with their own cleanup:

- email\_stats - Keeps only the statistics elements - in testing this reduces the table to single figure GB if the patch is run, compared to without it could be 10s/100s
- email\_stats\_data - Keeps the view in web browser tokens which are large. Has a separate configuration defaulting to 180 days for storage, so cleans up much faster than previously without impact
- email\_stats\_open\_details - Keeps the user agents on opens, again with a much shorter retention as it only seems to be used in contact view logs. This data tends to be minuscule in comparison.

Another benefit is hopefully reducing free space issues in email\_stats. Because open details are append only to another table, it means email\_stats should end up fairly static row lengths and less updates, meaning less fragmentation.

I think these groups of people would benefit from this idea:

System admins running Mautic

Why I think they would benefit from this idea:

Better control over database usage with additional retention for the big data such as view in browser tokens. Less fragmentation of tables.

Any code or resources to support this idea:

> <https://github.com/mautic/mautic/pull/15605>
>
> | Q | A
> | --------------------------------…------ | ---
> | Bug fix? (use the a.b branch) | ❌
> | New feature/enhancement? (use the a.x branch) | ✔️
> | Deprecations? | ❌
> | BC breaks? (use the c.x branch) | ❌
> | Automated tests included? | ✔️
> 
> \## Description
> 
> I've been investigating \`email\_stats\` usage and growth for some time and there's a few issues that cause it to bloat massively:
> \* \`tokens\` contains data that is readily available elsewhere, such as contact fields
> \* \`tokens\` contains data that is easy to calculate on-demand, such as tracking IDs
> \* \`tokens\` contains unnecessary entries such as an empty dynamic placeholders item
> \* \`open\_details\` contains a full User-Agent string for every open of an email by a lead
> \* Entries are never removed, even if associated leads are removed
> 
> \`tokens\` contributes to the significant majority of the table size. Fixing those issues is another task. But a cleanup would go a long way to keeping size under control.
> 
> The most common method of reducing the table size in the forums is to cleanup the \`tokens\` field data.
> 
> This PR implements that as a cleanup process called "compaction". It also implements the missing cleanup of entries. The open counts against the emails are incremented separately so I believe the cleanup after 7 years during \`maintenance:cleanup --days-old=2555\` would only impact reports for data going back longer than that. During this same maintenance period compaction takes place. \`tokens\` seems only ever used by "View in web browser" which should not be something a user needs to do on an email sent weeks ago. \`open\_details\` is used to show the user agent details for an open event, I think (couldn't quite find) in audit log for a lead - which again shouldn't be needed years later.
> 
> The default for compaction of \`open\_details\` and \`tokens\` is aggressive - and warrants discussion during review (180 days)! But it is configurable.
> 
> There is a huge risk to deployment here in that the alteration of \`email\_stats\` is indeed a large task - but I couldn't see any other way to do a fast compact without a column indicator. Indexing \`tokens\` and \`open\_details\` would just create massive indexes. So likely might need a warning? Unless there's a better approach?
> 
> Also, \*\*a table rebuild is likely still needed\*\* after this on a periodic basis to release disk space but at least it's a case of user's just running that task infrequently, and not needing to worry about running long-running queries against the database to reduce the data. They can just look at \`Data\_free\` and schedule that in. A future improvement could be to shift \`tokens\` and \`open\_details\` to their own table though - as then old compaction is just deletion which remove entire rows and the table space pages can then be reused effectively unlike with this current cleaning. One for another task, maybe alongside optimising \`tokens.\`
> 
> \### 📋 Steps to test this PR:
> 
> 1. Open this PR on Gitpod or pull down for testing locally (see docs on testing PRs \[here\](https://contribute.mautic.org/contributing-to-mautic/tester))
> 2. Configure compaction threshold to 1 day
> 3. Send some emails and wait a day
> 4. Send some more and wait a day
> 5. Send some more
> 6. Run \`maintenance:cleanup --days-old=2\`
> 7. Ensure email\_stats is reduced to remove stats for the first email
> 8. Ensure tokens and open details are empty for emails on the first and second email
> 
> (Might need tweak timings, or fake them yourself, as days-old might be inclusive)

Are you willing to work on this idea?:

Yes

What skills and resources do you need to explore this further?

Just some feedback and discussion to hear if this is useful to others.

I do plan to look further at the View in browser tokens in future though to further reduce tokens - as the vast majority of these tokens can be regenerated on-demand and do not need to be stored. I had a basic POC but this bit can get pretty large change as the way tokens are generated needs to be more controlled rather than the current mechanism where discovery and pattern replace for thing like tracking tokens (which are the biggest contributor if you have lots of links in emails) happens all in one sweep.

---

<div class="post-metadata">

### Author: ![ekke](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.mautic.org/ekke/32/296_2.png) [@ekke](https://forum.mautic.org/u/ekke)
#### Post date: [February 26, 2026, 10:42am UTC](https://forum.mautic.org/t/optimised-storage-for-email-statistics-view-in-browser-tokens-open-details/37273/2 "2026-02-26T10:42:20Z")

</div>

Hi there,  
I think [GitHub - Leuchtfeuer/mautic-housekeeping-bundle: Additional options for data cleanup and advanced database maintenance (beyond mautic:maintenance:cleanup)](https://github.com/Leuchtfeuer/mautic-housekeeping-bundle) is addressing this - allows to get rid of email-stats-tokens separately 🙂

---

<div class="post-metadata">

### Author: ![driskell](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.mautic.org/driskell/32/10405_2.png) [@driskell](https://forum.mautic.org/u/driskell)
#### Post date: [February 26, 2026, 11:10am UTC](https://forum.mautic.org/t/optimised-storage-for-email-statistics-view-in-browser-tokens-open-details/37273/3 "2026-02-26T11:10:03Z")

</div>

Ah interesting.

This brings it into core though which I think is desperately needed. And I have some more work I want to do to bring down token sizes 90+%. One key element of separate tables though is that this prevents fragmentation too as the cleanup is removing rows from one side of the data, rather than clearing out blocks within rows. With the bundle method I suspect it will cause a lot of wasted space until a rebuild, and that might be why that module provides an “optimise-tables” option. Problem for me is that an optimize table on my email\_stats would take the platform offline for 10m at least so we’d lose valuable open tracking

I’ll definitely take a closer look though.
