Skip to content

feat(envd): add optional EntryInfo to watch FilesystemEvent#2930

Open
mishushakov wants to merge 4 commits into
mainfrom
mishushakov/watchdir-entryinfo-option
Open

feat(envd): add optional EntryInfo to watch FilesystemEvent#2930
mishushakov wants to merge 4 commits into
mainfrom
mishushakov/watchdir-entryinfo-option

Conversation

@mishushakov
Copy link
Copy Markdown
Member

Adds an include_entryinfo flag to WatchDirRequest and CreateWatcherRequest; when set, each FilesystemEvent carries the affected entry's EntryInfo (new optional EntryInfo entry field). The entry is populated best-effort and left nil for remove/rename-away events where the path no longer exists, so the watch stream/watcher is never torn down by a stat of a gone file. Both the streaming WatchDir and the polling CreateWatcher/GetWatcherEvents paths are supported, with regenerated proto code in packages/envd and packages/shared. Bumps the envd version to 0.6.2 and adds watch_test.go covering entry-present, entry-absent, and removed-entry cases.

🤖 Generated with Claude Code

Add an include_entryinfo flag to WatchDirRequest and CreateWatcherRequest.
When set, each FilesystemEvent carries the affected entry's EntryInfo,
populated best-effort (left nil for remove/rename-away events where the
entry no longer exists at the path). Bumps envd version to 0.6.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cla-bot cla-bot Bot added the cla-signed label Jun 4, 2026
@cursor
Copy link
Copy Markdown

cursor Bot commented Jun 4, 2026

PR Summary

Low Risk
Backward-compatible optional API on filesystem watching with best-effort stat and new tests; no auth or critical-path changes.

Overview
Filesystem watch APIs can optionally attach full EntryInfo on each FilesystemEvent when clients set include_entry on WatchDir or CreateWatcher. Entry metadata is filled best-effort via stat; missing paths (e.g. after remove) leave entry unset without failing the watch. envd is released as 0.6.2 with regenerated protos and tests for include-on, include-off, and removed-file behavior.

Reviewed by Cursor Bugbot for commit 9449fdc. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 4, 2026

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
2733 5 2728 7
View the full list of 5 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestSandboxListPaginationRunningLargerLimit

Flake rate in main: 40.20% (Passed 879 times, Failed 591 times)

Stack Traces | 97.4s run time
=== RUN   TestSandboxListPaginationRunningLargerLimit
    sandbox_list_test.go:327: Created sandbox 1/12: i5lxn3rtsu4w1lkgbel3t
    sandbox_list_test.go:327: Created sandbox 2/12: i7vz0jdva41x7meu9trfg
    sandbox_list_test.go:327: Created sandbox 3/12: i2guvec0gr2xq6py0z2n8
    sandbox_list_test.go:327: Created sandbox 4/12: i7trx9to2ej1e6jyfp5eq
    sandbox_list_test.go:327: Created sandbox 5/12: i1x4ioqugyacakc1i09h6
    sandbox_list_test.go:327: Created sandbox 6/12: inhbb6cwjbv6wkyv2esx1
    sandbox_list_test.go:327: Created sandbox 7/12: idl5f97jbeu8kf8shgo9x
    sandbox_list_test.go:327: Created sandbox 8/12: i09rxm5hzf1nzrqftpgu0
    sandbox_list_test.go:327: Created sandbox 9/12: i86bg7ngb9hlul0qliuzc
    sandbox_list_test.go:327: Created sandbox 10/12: iuhqgej6dwgwqugqaj56b
    sandbox_list_test.go:327: Created sandbox 11/12: isnd7rkswzuaqnbr28l79
    sandbox_list_test.go:327: Created sandbox 12/12: isieid17u2di58014ttfu
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:340
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	"[]" should have 12 item(s), but has 0
    sandbox_list_test.go:330: 
        	Error Trace:	.../api/sandboxes/sandbox_list_test.go:330
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxListPaginationRunningLargerLimit
--- FAIL: TestSandboxListPaginationRunningLargerLimit (97.39s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity

Flake rate in main: 55.03% (Passed 872 times, Failed 1067 times)

Stack Traces | 62.3s run time
=== RUN   TestSandboxMemoryIntegrity
=== PAUSE TestSandboxMemoryIntegrity
=== CONT  TestSandboxMemoryIntegrity
    sandbox_memory_integrity_test.go:27: Build completed successfully
--- FAIL: TestSandboxMemoryIntegrity (62.30s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity/tmpfs_hash

Flake rate in main: 55.10% (Passed 862 times, Failed 1058 times)

Stack Traces | 188s run time
=== RUN   TestSandboxMemoryIntegrity/tmpfs_hash
=== PAUSE TestSandboxMemoryIntegrity/tmpfs_hash
=== CONT  TestSandboxMemoryIntegrity/tmpfs_hash
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{start:{pid:1257}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Total memory: 985 MB\nUsed memory before tmpfs mount: 194 MB\nFree memory before tmpfs mount: 790 MB\nMemory to use in integrity test (60% of free, min 64MB): 474 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"474+0 records in\n474+0 records out\n497025024 bytes (497 MB, 474 MiB) copied, 2.25559 s, 220 MB/s\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\tCommand being timed: \"dd if=/dev/urandom of=/mnt/testfile bs=1M count=474\"\n\tUser time (seconds): 0.00\n\tSystem time (seconds): 2.24\n\tPercent of CPU this job got: 98%\n\tElapsed (wall clock) time (h:mm:ss or m:ss): 0:02.27\n\tAverage shared text size (kbytes): 0\n\tAverage unshared data size (kbytes): 0\n\tAverage stack size (kbytes): 0\n\tAverage total size (kbytes): 0\n\tMaximum resident set size (kbytes): 2704\n\tAverage resident set size (kbytes): 0\n\tMajor (requiring I/O) page faults: 2\n\tMinor (reclaiming a frame) page faults: 342\n\tVoluntary context switches: 3\n\tInvoluntary context switches: 10\n\tSwaps: 0\n\tFile system inputs: 176\n\tFile system outputs: 0\n\tSocket messages sent: 0\n\tSocket messages received: 0\n\tSignals delivered: 0\n\tPage size (bytes): 4096\n\tExit status: 0\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Used memory after tmpfs mount and file fill: 673 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{end:{exited:true  status:"exit status 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] completed successfully in sandbox ibnfq0qsxcr8rodtzo9em
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1274}}
Executing command bash in sandbox imtpy6mc304nk61wziwlb (user: root)
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{data:{stdout:"85d0e52a24f7f7d29323c25e15e4aff1e21d0eb6d361496c1bcf40f7ed7b061c\n"}}
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{end:{exited:true  status:"exit status 0"}}
    sandbox_memory_integrity_test.go:80: Command [bash] completed successfully in sandbox ibnfq0qsxcr8rodtzo9em
    sandbox_memory_integrity_test.go:80: Command [bash] output: event:{start:{pid:1278}}
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
Executing command bash in sandbox ibnfq0qsxcr8rodtzo9em (user: root)
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:81
        	            				.../hostedtoolcache/go/1.26.3.../src/runtime/asm_amd64.s:1771
        	Error:      	Received unexpected error:
        	            	failed to execute command bash in sandbox ibnfq0qsxcr8rodtzo9em: unavailable: HTTP status 502 Bad Gateway
    sandbox_memory_integrity_test.go:110: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:78
        	            				.../tests/orchestrator/sandbox_memory_integrity_test.go:110
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxMemoryIntegrity/tmpfs_hash
--- FAIL: TestSandboxMemoryIntegrity/tmpfs_hash (187.62s)
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestEnvdAccessTokenAutoResumeViaProxy

Flake rate in main: 40.10% (Passed 868 times, Failed 581 times)

Stack Traces | 11s run time
=== RUN   TestEnvdAccessTokenAutoResumeViaProxy
=== PAUSE TestEnvdAccessTokenAutoResumeViaProxy
=== CONT  TestEnvdAccessTokenAutoResumeViaProxy
    traffic_access_token_test.go:357: 
        	Error Trace:	.../tests/proxies/traffic_access_token_test.go:357
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:3002/health": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
        	Test:       	TestEnvdAccessTokenAutoResumeViaProxy
--- FAIL: TestEnvdAccessTokenAutoResumeViaProxy (11.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/proxies::TestSandboxAutoResumeViaProxy

Flake rate in main: 40.60% (Passed 866 times, Failed 592 times)

Stack Traces | 17.2s run time
=== RUN   TestSandboxAutoResumeViaProxy
=== PAUSE TestSandboxAutoResumeViaProxy
=== CONT  TestSandboxAutoResumeViaProxy
    auto_resume_test.go:116: 
        	Error Trace:	.../tests/proxies/auto_resume_test.go:116
        	Error:      	Received unexpected error:
        	            	Get "http://localhost:3002": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
        	Test:       	TestSandboxAutoResumeViaProxy
--- FAIL: TestSandboxAutoResumeViaProxy (17.16s)
Executing command cat in sandbox iz9f85hmy0nrlfi4ycqgo (user: root)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Failing the entire watch stream when retrieving the optional entry info fails makes the watcher fragile. Since entry info is intended to be best-effort, any error from eventEntryInfo should be ignored, leaving the entry field nil so the stream can continue processing other events. Similarly, terminating the background watcher goroutine when retrieving the optional entry info fails causes a resource leak and stops all future event processing; this error should also be ignored to keep the watcher active.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/envd/internal/services/filesystem/watch.go Outdated
Comment thread packages/envd/internal/services/filesystem/watch_sync.go
mishushakov and others added 2 commits June 4, 2026 19:28
Match the FilesystemEvent.entry field name for consistency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Failing to stat the affected entry no longer tears down the watch stream
or the polling watcher goroutine (which previously leaked the fsnotify
watcher). The entry is left unset and non-NotFound errors are logged at
warn level. Addresses PR review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread packages/envd/internal/services/filesystem/utils.go Outdated
@mishushakov mishushakov marked this pull request as ready for review June 4, 2026 18:23
The errors.As type assertion was always true since entryInfo always
returns a connect.Error; only the code matters. Addresses PR feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant