Skip to content

Tmp#17

Merged
jeme merged 8 commits intomainfrom
tmp
May 7, 2025
Merged

Tmp#17
jeme merged 8 commits intomainfrom
tmp

Conversation

@jeme
Copy link
Copy Markdown
Contributor

@jeme jeme commented May 7, 2025

Summary by CodeRabbit

  • New Features
    • Added the ability to lock and unlock the writer manager, improving control over index writer creation.
  • Bug Fixes
    • Enhanced synchronization and thread safety during lease termination and disposal.
    • Improved handling and diagnostics for disposal and commit operations, providing clearer debug information in case of errors.
  • Refactor
    • Updated lease recall methods to return recalled items for better tracking.
    • Adjusted test directory creation to use a dedicated subfolder for improved organization.
  • Tests
    • Improved test diagnostics and reduced test duration for faster feedback.
  • Chores
    • Added explicit locking around delete operations to ensure safe resource management.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2025

Warning

Rate limit exceeded

@jeme has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 24 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4a1e798 and ce91bbd.

📒 Files selected for processing (2)
  • src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (6 hunks)
  • src/DotJEM.Json.Index2/Leases/LeaseManager.cs (7 hunks)

Walkthrough

The changes introduce enhanced diagnostics, synchronization, and resource management across the index writer and lease management subsystems. New methods for locking and unlocking writer creation were added, lease recall now returns recalled items, and additional debug output and stack traces assist in tracing disposal and commit operations. Test code was updated for improved timing and logging.

Changes

File(s) Change Summary
src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs
src/DotJEM.Json.Index2/Storage/IJsonIndexStorageManager.cs
Added Lock() and Unlock() methods to IIndexWriterManager and their implementation in IndexWriterManager. Writer creation is now blocked/unblocked using a ManualResetEventSlim. The storage manager now locks and unlocks the writer manager during delete operations. Debug output added for writer lifecycle events.
src/DotJEM.Json.Index2/IO/IndexWriterSafeProxy.cs Added unique instance IDs and disposal stack trace capture for each proxy. Enhanced Dispose and Commit methods with debug logging, including stack traces when exceptions occur. Improved diagnostics for commit/disposal misuse.
src/DotJEM.Json.Index2/Leases/LeaseManager.cs RecallAll() now returns recalled items instead of void. Lease and time-limited lease classes use wait handles (ManualResetEventSlim/AutoResetEvent) for coordinated termination and disposal. Synchronization improved to ensure proper resource cleanup.
src/DotJEM.Json.Index2.Management.Test/JsonIndexManagerTest.cs Test updated: execution time limited to 12 minutes, main loop duration reduced, explicit commit before event subscription, enhanced debug logging for exceptions, and test directory now created under a dedicated subfolder.
src/DotJEM.Json.Index2.Test/JsonIndexTest.cs Added a blank line after an assertion in one test method; no logic changes.

Sequence Diagram(s)

sequenceDiagram
    participant Test as JsonIndexManagerTest
    participant Manager as IndexWriterManager
    participant Proxy as IndexWriterSafeProxy
    participant LeaseMgr as LeaseManager
    participant Storage as IJsonIndexStorageManager

    Test->>Manager: Commit()
    Manager->>Proxy: Commit()
    Proxy->>Proxy: Log commit attempt (with id)
    Proxy->>Proxy: Try inner commit
    alt ObjectDisposedException
        Proxy->>Proxy: Log disposal stack trace
        Proxy->>Test: Rethrow exception
    end

    Test->>Manager: TakeSnapshotAsync / ResetIndexAsync
    Manager->>LeaseMgr: RecallAll()
    LeaseMgr->>Test: Return recalled leases

    Test->>Storage: Delete()
    Storage->>Manager: Lock()
    Storage->>LeaseMgr: RecallAll()
    Storage->>Manager: Close()
    Storage->>provider: Delete()
    Storage->>Manager: Unlock()
Loading

Poem

In the warren of code, the writers now wait,
With locks and events, they coordinate fate.
Stack traces hop when errors appear,
And leases recall what once was near.
Debugging is brighter, disposal in view—
A rabbit's delight in a system anew!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

# Conflicts:
#	src/DotJEM.Json.Index2/Storage/IIndexStorageProvider.cs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🔭 Outside diff range comments (1)
src/DotJEM.Json.Index2.Management.Test/JsonIndexManagerTest.cs (1)

41-58: ⚠️ Potential issue

Potential data race on disposedEvent / exceptionEvent

Both variables are mutated from the InfoStream subscription (likely on a different thread) and read from the test thread without any synchronisation.
This may lead to torn reads or missed writes.
Mark the fields volatile or use an Interlocked.Exchange/Interlocked.CompareExchange pattern to avoid the race:

-InfoStreamExceptionEvent? disposedEvent = null;
-InfoStreamExceptionEvent? exceptionEvent = null;
+volatile InfoStreamExceptionEvent? disposedEvent = null;
+volatile InfoStreamExceptionEvent? exceptionEvent = null;
♻️ Duplicate comments (1)
src/DotJEM.Json.Index2/Leases/LeaseManager.cs (1)

194-203: Same missing-lock issue in TimeLimitedLease.WithLock

Replicate the locking fix suggested for Lease.
Otherwise concurrent access during Terminate() can expose partially-disposed state.

🧹 Nitpick comments (11)
src/DotJEM.Json.Index2.Management.Test/JsonIndexManagerTest.cs (3)

24-25: Consider tightening (or parameterising) the MaxTime attribute

MaxTime(1000*60*12) allows the test to run for up to 12 minutes.
Even for an [Explicit] test this is unusually long and can mask hangs.
If the long timeout is required, consider extracting it to a constant / config so it is easy to adjust (e.g. when running in CI vs. locally).


65-75: Long-running busy-loop can slow the test suite

Even reduced to 1 minute, this loop can still dominate test execution time in CI.
Consider:
• lowering the duration further,
• using a cancellation token that is signalled when either event occurs, or
• parametrising the duration so CI can run a shorter version.


80-88: Catch block only logs e.Message

Important diagnostic information (stack trace, inner exceptions) is discarded. Replace with Debug.WriteLine(e) or Debug.WriteLine(e.ToString()) to preserve full context.

-Console.WriteLine(e.Message);
+Debug.WriteLine(e);
src/DotJEM.Json.Index2/Storage/IIndexStorageProvider.cs (1)

11-14: Path property name may collide with System.IO.Path in call-sites

Although legal, using the identifier Path on an interface that is widely consumed can cause readability issues when mixed with using System.IO;.
Consider renaming to something more specific like StoragePath to avoid ambiguity.

src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (2)

76-82: Re-entrant lock is fine, but redundant

Lease() acquires writerPadLock and then calls Writer which will try to acquire the same lock. While Monitor is re-entrant, it incurs extra overhead.
You can call Writer outside the lock and only lock when interacting with leaseManager.


112-115: writers diagnostic may under-report live writers

Because dead references accumulate, writers.Count grows unbounded and the “alive” count diverges.
After counting, prune the list:

lock (writersPadlock)
{
    writers.RemoveAll(w => !w.TryGetTarget(out _));
}
src/DotJEM.Json.Index2/IO/IndexWriterSafeProxy.cs (3)

15-17: Thread-safety of whoDisposed is not guaranteed

whoDisposed is assigned in Dispose()/Dispose(bool) and read back from Commit().
If Commit() races with Dispose() from another thread, there is a small window where Dispose() has not yet assigned the field, causing a null printout and losing the diagnostic value. A simple volatile declaration (or an Interlocked.Exchange) makes the intent explicit and prevents re-ordering.

-    private StackTrace whoDisposed;
+    private volatile StackTrace? whoDisposed;

37-46: Inconsistent logging API & stack-trace loss

Dispose… uses Debug.WriteLine while Commit() uses Console.WriteLine.
Mixing sinks complicates log collection, especially in library code where consumers cannot easily redirect Console.
I recommend routing everything through the same mechanism (e.g. Debug, or the project’s logger), and emitting the stack-trace directly instead of relying on ToString() (which omits file/line information in Release builds).


199-217: Avoid swallowing context when re-throwing

Inside the catch (ObjectDisposedException e) block you print diagnostics and then throw;.
That preserves the original stack, which is good, but all the extra information you just printed is lost for callers who rely on exception data (e.g. telemetry pipelines that do not capture stdout).
Consider attaching the disposal stacktrace to the exception Data dictionary before re-throwing; no API breakage and the info stays with the exception.

catch (ObjectDisposedException e)
{
    Console.WriteLine("Who disposed me:");
    Console.WriteLine(whoDisposed);
    …
-    throw;
+    e.Data["DisposalStack"] = whoDisposed;
+    e.Data["CommitCallerStack"] = new StackTrace();
+    throw;
}
src/DotJEM.Json.Index2/Leases/LeaseManager.cs (2)

45-53: Potential race condition when copying leases

You clear leases inside the lock and then iterate copy outside.
OnReturned (invoked by Terminate()) will attempt to Remove the same lease from the now-empty list.
While this is functionally harmless, it still acquires the lock for every lease and performs a linear scan on an empty list.
Consider removing the callback invocation or guarding OnReturned with a flag to skip removal when the manager has already recalled everything.


132-137: returned.Wait(500) can block forever on lost signal

If the consumer never calls Dispose(), the ManualResetEventSlim is never set and Terminate() waits the full 500 ms for each lease, turning RecallAll() into an O(n) delay.
Either:

  1. Use a much shorter timeout (e.g. 10 ms) and loop a few times, or
  2. Skip waiting when nothing is listening.

This avoids holding the manager hostage when mis-behaving clients forget to dispose.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f0a730 and c6c102d.

📒 Files selected for processing (9)
  • src/DotJEM.Json.Index2.Management.Test/JsonIndexManagerTest.cs (6 hunks)
  • src/DotJEM.Json.Index2.Management/Writer/IJsonIndexWriter.cs (1 hunks)
  • src/DotJEM.Json.Index2.Test/JsonIndexTest.cs (1 hunks)
  • src/DotJEM.Json.Index2/IO/IndexWriterSafeProxy.cs (4 hunks)
  • src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (6 hunks)
  • src/DotJEM.Json.Index2/Leases/Lease.cs (1 hunks)
  • src/DotJEM.Json.Index2/Leases/LeaseManager.cs (7 hunks)
  • src/DotJEM.Json.Index2/Storage/IIndexStorageProvider.cs (2 hunks)
  • src/DotJEM.Json.Index2/Storage/IJsonIndexStorageManager.cs (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/DotJEM.Json.Index2/Leases/Lease.cs (1)
src/DotJEM.Json.Index2/Leases/LeaseManager.cs (4)
  • WithLock (121-124)
  • WithLock (199-202)
  • TOut (116-119)
  • TOut (194-197)
src/DotJEM.Json.Index2/Storage/IJsonIndexStorageManager.cs (2)
src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (6)
  • Lock (19-19)
  • Lock (42-45)
  • Close (17-17)
  • Close (93-115)
  • Unlock (20-20)
  • Unlock (46-49)
src/DotJEM.Json.Index2/Storage/IIndexStorageProvider.cs (3)
  • Delete (13-13)
  • Delete (21-24)
  • Delete (39-56)
src/DotJEM.Json.Index2/Storage/IIndexStorageProvider.cs (3)
src/DotJEM.Json.Index2.Contexts/Storage/IStorageProviderFactory.cs (3)
  • IIndexStorageProvider (8-8)
  • IIndexStorageProvider (15-15)
  • IIndexStorageProvider (28-28)
src/DotJEM.Json.Index2/Storage/IJsonIndexStorageManager.cs (2)
  • Delete (18-18)
  • Delete (71-93)
src/DotJEM.Json.Index2.Snapshots/Zip/ZipFileSnapshot.cs (1)
  • Delete (32-35)
src/DotJEM.Json.Index2/Leases/LeaseManager.cs (2)
src/DotJEM.Json.Index2/Leases/Lease.cs (3)
  • TOut (14-14)
  • WithLock (15-15)
  • TryRenew (12-12)
src/DotJEM.Json.Index2/Util/Disposable.cs (2)
  • Dispose (9-17)
  • Dispose (19-22)
🔇 Additional comments (6)
src/DotJEM.Json.Index2.Test/JsonIndexTest.cs (1)

86-86: Formatting change looks good

This blank line addition maintains consistent spacing between test methods.

src/DotJEM.Json.Index2.Management/Writer/IJsonIndexWriter.cs (1)

154-155: Improved thread safety for commit operations

The change from direct access to using WithLock ensures thread-safe commit operations by properly synchronizing access to the leased writer. This prevents potential race conditions when multiple threads attempt to commit simultaneously.

src/DotJEM.Json.Index2/Leases/Lease.cs (1)

14-15: Good addition of thread-safe resource access methods

These new methods provide a standardized way to perform operations on leased resources within a lock context, ensuring thread safety. The two method signatures accommodate both operations that return values and those that don't.

src/DotJEM.Json.Index2/Storage/IJsonIndexStorageManager.cs (2)

83-83: Good synchronization for deletion operations

Adding the WriterManager lock prevents new writers from being created during deletion, avoiding potential race conditions where writers might access files being deleted.


91-91: Proper lock release after deletion

The WriterManager is correctly unlocked after all deletion operations are complete, ensuring the system returns to normal operation.

src/DotJEM.Json.Index2.Management.Test/JsonIndexManagerTest.cs (1)

39-40: Verify that the explicit index.Commit() does not undermine the test intent

Committing the index before RunAsync() forces a writer to be created/closed outside the manager’s lifecycle, potentially invalidating the scenario “writer should not be disposed while the manager is running”.
Double-check that this early commit is really needed; otherwise remove it or move the call inside RunAsync() where the manager controls it.

Comment thread src/DotJEM.Json.Index2/Storage/IIndexStorageProvider.cs Outdated
Comment thread src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs
Comment thread src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs
Comment thread src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs
Comment thread src/DotJEM.Json.Index2/Leases/LeaseManager.cs
Comment thread src/DotJEM.Json.Index2/Leases/LeaseManager.cs
Comment thread src/DotJEM.Json.Index2/Leases/LeaseManager.cs Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (2)

40-41: writers is still a non-thread-safe static list

Previous feedback already pointed out that concurrent Add and enumeration can corrupt the List<T> instance (and enumeration at lines 111-113 may throw).
Protect the collection with a dedicated lock or swap it for a concurrent container.


42-49: Lock() / Unlock() should also hold writerPadLock

Resetting the event without taking the same mutex used during writer creation leaves a race window:

  1. Thread-A enters Writer, passes reset.Wait() (signalled), then is pre-empted.
  2. Thread-B calls Lock(), resets the event, and proceeds.
  3. Thread-A resumes and creates a writer even though creation was supposed to be blocked.

Acquiring writerPadLock inside Lock()/Unlock() (or moving reset.Wait() outside the writerPadLock section) would close this gap.

🧹 Nitpick comments (1)
src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (1)

63-67: Consider timeout / cancellation for reset.Wait()

reset.Wait() can block indefinitely if a caller forgets to call Unlock() (e.g., due to an exception).
Passing a reasonable timeout or a CancellationToken will make the system fail fast and easier to diagnose.

Example:

reset.Wait(TimeSpan.FromSeconds(30), cancellationToken);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2638441 and 4a1e798.

📒 Files selected for processing (1)
  • src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (5)
src/DotJEM.Json.Index2/Leases/LeaseManager.cs (9)
  • ILease (13-13)
  • ILease (14-14)
  • ILease (26-29)
  • ILease (31-34)
  • ILease (56-63)
  • Lease (78-138)
  • Lease (109-113)
  • Dispose (129-137)
  • Dispose (207-215)
src/DotJEM.Json.Index2/Leases/Lease.cs (1)
  • ILease (18-18)
src/DotJEM.Json.Index2/IO/IndexWriterSafeProxy.cs (2)
  • Dispose (35-40)
  • Dispose (42-47)
src/DotJEM.Json.Index2/IO/IIndexWriter.cs (2)
  • Dispose (14-14)
  • Dispose (15-15)
src/DotJEM.Json.Index2/Util/Disposable.cs (2)
  • Dispose (9-17)
  • Dispose (19-22)
🔇 Additional comments (1)
src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs (1)

76-82: 👍 Lease acquisition is now protected by writerPadLock

Wrapping the lease creation in the same lock that guards writer removes a potential race between lease creation and writer replacement. Nice improvement.

Comment thread src/DotJEM.Json.Index2/IO/JsonIndexWriterManager.cs
@jeme jeme merged commit d22ad98 into main May 7, 2025
5 checks passed
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.

1 participant