Skip to content

Watcher File Change Events Trigger Unbounded Concurrent Reimports, without any Debounce or Limit #297

Open

Description

@rootp1

Describe the bug

Every Write event spawns a new goroutine with no debounce, no concurrency limit, no deduplication. When a file is saved in an IDE, it typically generates 2-5 write events in rapid succession
(write temp file, rename, metadata update). A cp or sed -i generates similar bursts. Each event spawns a separate HTTP upload to Microcks.

Why Critical:

  1. Thundering herd: 10 files changed simultaneously leads to 10+ goroutines, 10+ concurrent uploads, potentially overwhelming the Microcks server.
  2. Race condition on the server: Two concurrent uploads of the same artifact can create conflicting service definitions, especially for large specs that take seconds to process.
  3. No error aggregation: Each goroutine logs errors independently to stdout with no structured tracking.
  4. No backpressure: If the server is slow, goroutines accumulate indefinitely, this is a goroutine leak.
  5. fsnotify on directories: Some editors replace files rather than modifying in-place, which can cause the watcher to lose track of the file entirely (the inode changes).

Expected behavior

When watching files for changes, the CLI should debounce file system events (e.g., waiting ~200-500ms after the last edit before triggering an import) to ensure only a single logical save triggers one import. It should also limit concurrency or sequence the TriggerImport calls to avoid overwhelming the Microcks server. Furthermore, the watcher should correctly handle atomic file saves (which often trigger fsnotify.Rename or fsnotify.Create events) so it doesn't lose track of files when they are overwritten.

Actual behavior

Currently in pkg/watcher/watchManager.go, the Run() loop listens specifically for fsnotify.Write and immediately spawns a new, unbounded goroutine (go TriggerImport(entry)) for every single event.
Modern IDEs and tools (like sed -i or VS Code) trigger multiple rapid file system events when a file is saved (temp file write, rename, metadata update). This results in a "thundering herd" where multiple concurrent HTTP requests to upload the exact same artifact are fired at the Microcks server within milliseconds. This causes:

  • Race conditions on the Microcks server resulting in potential database issues or duplicated logic.
  • Goroutine leaks / absence of backpressure if the server is slow to respond.
  • Loss of file tracking if the IDE performs an atomic save (replacing the file and changing its inode), because the CLI ignores non-Write operations.

How to Reproduce?

Steps to reproduce the issue:

  1. Start the microcks-cli watcher on a configuration tracking an API specification (e.g., using microcks-cli test ... or your dedicated watch command).
  2. Open the tracked API specification file in an editor like VS Code and hit Save, or simulate rapid disk writes via terminal: for i in {1..5}; do touch my-api-spec.yaml; done
  3. Inspect the microcks-cli console output. You will see multiple [INFO] Re-importing changed file: logs simultaneously, meaning multiple concurrent HTTP uploads for the same file are executed.
2026/05/05 23:29:07 [INFO] Watcher added on /root/cncf/microcks-cli/test-spec.yaml
[INFO] microcks-watcher started...
[INFO] Re-importing changed file: /root/cncf/microcks-cli/test-spec.yaml
[WARN] Error re-importing /root/cncf/microcks-cli/test-spec.yaml: Post "/test-context/api/artifact/upload": unsupported protocol scheme ""                      
[INFO] Re-importing changed file: /root/cncf/microcks-cli/test-spec.yaml
[WARN] Error re-importing /root/cncf/microcks-cli/test-spec.yaml: Post "/test-context/api/artifact/upload": unsupported protocol scheme ""                      
[INFO] Re-importing changed file: /root/cncf/microcks-cli/test-spec.yaml
[WARN] Error re-importing /root/cncf/microcks-cli/test-spec.yaml: Post "/test-context/api/artifact/upload": unsupported protocol scheme ""                      
...

Microcks version or git rev

  • Microcks CLI version or git rev: master branch - Install method: Local build from source

Install method (docker-compose, helm chart, operator, docker-desktop extension,...)

No response

Additional information

The root cause is in pkg/watcher/watchManager.go:

case event := <-wm.fileWatcher.Events:
    if event.Op&fsnotify.Write == fsnotify.Write {
        // ...
        } else {
            wm.lock.Lock()
            entry, exists := wm.watchEntries[event.Name]
            wm.lock.Unlock()
            if exists {
                go TriggerImport(entry) // Spawned instantly per event
            }
        }

Activity

  1. github-actions commented on May 5, 2026

    @github-actions

    👋 @rootp1

    Welcome to the Microcks community! 💖

    Thanks and congrats 🎉 for opening your first issue here! Be sure to follow the issue template or please update it accordingly.

    📢 If you're using Microcks in your organization, please add your company name to this list. 🙏 It really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact.

    If you need to know why and how to add yourself to the list, please read the blog post "Join the Microcks Adopters list and Empower the vibrant open source Community 🙌"

    Hope you have a great time there!

    🌟 ~~~~~~~~~ 🌟

    📢 If you like Microcks, please ⭐ star ⭐ our repo to support it!

    🙏 It really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact.

  2. rootp1 commented on May 5, 2026

    @rootp1
    Author

    Hey @Harsh4902; What are your thoughts?
    If this issue is helpful, I can submit a PR

  3. yada commented on Oct 14, 2026

    @yada
    Member

    Reproduced on master (f9550f5) against a real Microcks server, using microcks import <file>:true --watch.

    • A burst of 20 quick writes (one logical save) triggered 16 re-imports on macOS and 22 re-imports on Linux, with 22 successful uploads logged.
    • Even a single echo >> file triggered 2 re-imports on Linux.

    Cause: every fsnotify.Write event runs go TriggerImportWithContext(...) in pkg/watcher/watchManager.go with no debounce, no per-file deduplication and no concurrency limit.

    Suggested fix: debounce per file (for example about 300 ms after the last event) and run one import at a time per file. This shares a code area with #432 (atomic saves), so one PR could cover both.

  4. yada commented on Oct 17, 2026

    @yada
    Member

    Update: PR #586 ("File watcher stops triggering after first save") looks like it addresses this. I built its head and ran import --watch against a real server on macOS, compared with master:

    • Plain write, atomic save (temp file plus mv), write after the atomic save, second atomic save: 4 re-imports with the PR, versus 2 on master (both atomic saves lost).
    • A burst of 20 appends: 1 re-import with the PR, versus 12 on master.

    Not tested: Linux (inotify), where the watcher died after an atomic save on master. That should be checked before merging.

    Disclosure: this comment was drafted with AI assistance, and the results were run locally.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions