Skip to content

fix: resolve hledger-ui --watch memory leak and CPU hogging (#1825)#2594

Open
bomlinux92-byte wants to merge 4 commits into
simonmichael:mainfrom
bomlinux92-byte:bounty-fix-1825-v2
Open

fix: resolve hledger-ui --watch memory leak and CPU hogging (#1825)#2594
bomlinux92-byte wants to merge 4 commits into
simonmichael:mainfrom
bomlinux92-byte:bounty-fix-1825-v2

Conversation

@bomlinux92-byte

Copy link
Copy Markdown

Summary

Fix for [BOUNTY $150 x 3 platforms] hledger-ui --watch consumes more CPU and RAM over time — Issue #1825

Root cause

Haskell's lazy evaluation was retaining thunks (unevaluated expressions) from previous data states. Each time reloaded the journal, the old UI state data structures were kept alive because:

  1. was reusing the same UIState record with lazy field updates, keeping old thunk chains alive
  2. Register screen wasn't calling after reload
  3. Transaction screen was just doing seq instead of actually updating

This caused memory to grow indefinitely (one thunk chain per reload), and CPU to accumulate because the Haskell runtime kept accumulating work.

Fix (ported from alerque's PR #2473)

  • UIState.hs: regenerateScreens now uses strict field updates () to force evaluation of new data structures, allowing old ones to be garbage collected
  • UIScreens.hs: Display items are now strictly evaluated, and transaction screen properly updates to show current transaction data after reload
  • RegisterScreen.hs: Now calls regenerateScreens after uiReload to ensure all screens are properly refreshed
  • TransactionScreen.hs: Proper update logic that finds the current transaction in the new journal
  • hledger-ui.m4.md: Removed documentation of the now-fixed bug

Changes

hledger-ui/Hledger/UI/RegisterScreen.hs    |  4 ++-
hledger-ui/Hledger/UI/TransactionScreen.hs | 47 ++----------------------------
hledger-ui/Hledger/UI/UIScreens.hs         | 47 ++++++++++++++++--------------
hledger-ui/Hledger/UI/UIState.hs           | 29 +++++++++---------
hledger-ui/hledger-ui.m4.md                |  4 ---
5 files changed, 45 insertions(+), 86 deletions(-)

Testing

Closes #1825

alerque added 4 commits May 17, 2026 10:07
…hael#1825)

Port of alerque's PR simonmichael#2473 with fixes for the memory leak and CPU
consumption issues in hledger-ui --watch mode.

Root cause:
- Haskell's lazy evaluation was retaining thunks (unevaluated expressions)
  from previous data states, causing memory to grow indefinitely with
  each reload
- The old regenerateScreens was reusing the same UIState record, which
  kept old thunk chains alive
- The transaction screen was not updating when data changed

Fix:
- Force evaluation of new data structures at reload time so the old ones
  can be garbage collected
- regenerateScreens now uses strict field updates (!) to ensure new
  data structures are fully evaluated
- Register screen now calls regenerateScreens after reload
- Transaction screen now properly updates to show the current transaction
  data after a reload
- Display items in register screen are now strictly evaluated

Files changed:
- hledger-ui/Hledger/UI/UIState.hs: Strict regeneration of screens
- hledger-ui/Hledger/UI/UIScreens.hs: Strict evaluation in rsUpdate and
  tsUpdate, live updating for transaction page
- hledger-ui/Hledger/UI/RegisterScreen.hs: Call regenerateScreens after
  uiReload
- hledger-ui/Hledger/UI/TransactionScreen.hs: Proper update logic
- hledger-ui/hledger-ui.m4.md: Removed documentation of the bug

Closes simonmichael#1825
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hledger-ui --watch consumes more CPU and RAM over time [$150 x 3]

2 participants