Skip to content

Game-setting locale indexer re-runs itself once a second on Linux (inotify IN_OPEN feedback loop) #7624

Description

@Unmade760

Game-setting locale indexer re-runs itself once a second on Linux (inotify IN_OPEN feedback loop)

Summary

On Linux the "Game setting locales" indexer runs continuously for as long as the app is open, about once per second, with no file changes in any instance. Each pass reads every instance's options.txt; that read is itself an inotify event; the instance file watcher sees options.txt and queues another index pass. Each pass also emits game-option-locales-updated to the frontend, so the WebKit process stays busy too.

Measured on 0.21.3 and 0.21.4 (Flatpak, Fedora 44, kernel 7.2): 495 passes in the first 5 minutes of one session, 3333 in a longer one, 137 in the first 90 s after updating to 0.21.4. Main process at ~96 % CPU and WebKitWebProcess at ~95 % (8 JSC Heap Helper threads at 85 % each, 2 GB heap) while it runs. UI is sluggish the whole time.

Log pattern

INFO index_installed_sources: ... Game setting locales: indexing installed instances instances=2
INFO index_installed_sources: ... snapshot loaded instance_id="local:..." game_version="1.20.1" mods=446
INFO index_installed_sources: ... snapshot loaded instance_id="local:..." game_version="26.2" mods=68
INFO index_installed_sources: ... origin resolution finished pinned=0 unresolved=133
INFO ... Game setting locales: indexing pass finished elapsed_ms=1116
INFO ... Game setting locales: emitted game-option-locales-updated
INFO init_watcher: ... Game setting locales: indexing queued started=true
INFO init_watcher: ... Game setting locales: indexing queued started=true
INFO index_installed_sources: ... indexing installed instances instances=2
... repeats indefinitely, ~1 Hz

The two indexing queued lines per cycle are one per instance (two options.txt files). Note every queue comes from the init_watcher span; no label request received lines, so the frontend is not the trigger.

Evidence that no file is being written

strace -f -e openat,rename,renameat2,utimensat,unlink,unlinkat on the backend for 6 s: no write-mode opens, renames or unlinks anywhere under profiles/. Only O_RDONLY opens of options.txt and pack directories (16–19 per instance in 6 s).

inotifywait -m -e open,close_nowrite,access profiles/<instance>/options.txt:

11:12:22 OPEN            options.txt
11:12:22 ACCESS          options.txt
11:12:22 CLOSE_NOWRITE   options.txt
11:12:22 OPEN            options.txt
...

inotifywait -r -m -e create,delete,modify,moved_to,moved_from,attrib,close_write profiles/ over the same period: no events on options.txt at all.

Cause

  1. packages/app-lib/src/api/instance/synced_options/game_options/locales/mod.rs: index_installed_sources calls read_document(&options_path(...)) for every instance.
  2. notify 8.2.0's inotify backend includes WatchMask::OPEN in its watch mask (notify/src/inotify.rs, add_single_watch), so that read produces an EventKind::Access(Open) event.
  3. notify-debouncer-mini 0.7.0 discards the event kind, so the callback in packages/app-lib/src/state/instances/watcher.rs cannot distinguish a read from a write. Its filter (watcher.rs ~L164-180) matches the options.txt file name and calls crate::api::instance::queue_game_locale_index().
  4. The indexer task (start_game_locale_indexer) wakes, sleeps 300 ms, runs the pass, emits the event, and step 1 repeats.

Linux only: FSEvents and ReadDirectoryChangesW do not report opens, so macOS and Windows never enter the loop.

The same mechanism presumably fires reconcile_synced_option_file for command_history.txt, hotbar.nbt and servers.dat whenever anything reads them, but the locale indexer is the one that reads its own trigger file.

Suggested fix

Either of:

  • Switch the instance watcher to notify-debouncer-full (which keeps EventKind) and ignore EventKind::Access(_) before the path matching, or filter access events out via notify::Config / the debouncer's underlying watcher config where available.
  • Or, keep the mini debouncer but make the locale indexer not read options.txt through a watched path (e.g. snapshot the file contents once into the DB and have the watcher pass the changed document in), or add a cheap guard: skip queuing when the indexer itself is mid-pass and the file's mtime has not changed since the last pass.

Also worth debouncing the game-option-locales-updated emit so a burst of passes does not become a burst of frontend refreshes.

Environment

  • Modrinth App 0.21.3 and 0.21.4, Flatpak com.modrinth.ModrinthApp (Flathub)
  • Fedora 44, Linux 7.2.4, GNOME Wayland, 16 GB RAM
  • 2 instances: Fabulously Optimized (26.2, 68 mods) and Prominence II Hasturian Era (1.20.1, 446 mods)
  • Full session logs available on request (launcher_logs/session_*.log, 1–8 MB, almost entirely this loop)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions