Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Factored out WeakDictionary in favor of weak events using Prism.Core #613

Merged
merged 14 commits into from
Feb 9, 2022

Conversation

NightOwl888
Copy link
Contributor

Closes #256, #604, and #605.

This removes the Lucene.Net.Support.WeakDictionary<TKey, TValue> class (and its tests) that were carried over from Lucene.Net 3.0.3, and completely replaces it on .NET Standard 2.0 and .NET Framework with ConditionalWeakTable<TKey, TValue>. Since the enumerator was not available on .NET Standard 2.0 and prior, this workaround uses weak events from Prism.Core to either call methods or return strong references to types that are inside of the ConditionalWeakTable<TKey, TValue>.

This adds an AddOrUpdate extension method (non-threadsafe) for cases where a "put" is required.

Also adds Lucene.Net.Util.ExcludeFromRamUsageEstimationAttribute to mark fields so they don't get included in the calculation (which prevents infinite recursion on the event fields).

…ions class to provide a workaround for the lack of AddOrUpdate on .NET Standard 2.0 and prior
…Factored out WeakDictionary and use external locking to fixup AddOrUpdate where it is not supported.
…tionary and use external locking to fixup AddOrUpdate where it is not supported.
…lude fields from being considered in the calculation
…gator to handle getting estimated RAM usage on .NET Standard 2.0 and .NET Framework
… retrieve weak DocIdSet references on .NET Standard 2.0 and .NET Framework
…tting parent readers weakly on .NET Standard 2.0 and .NET Framework
…e AddCacheEntries by retrieving the keys via weak events and then updating the values one at a time on .NET Standard 2.0 and .NET Framework.
@rclabo
Copy link
Contributor

rclabo commented Jan 31, 2022

WOW! You are amazing!

@laimis
Copy link
Contributor

laimis commented Jan 31, 2022

I second the WOW! comment, can't believe you find a solution for this...

@eladmarg
Copy link
Contributor

eladmarg commented Feb 1, 2022

Even though it's not relevant for me (I'm using net core for years),
I'm really curious, did this solve the memory leak issue?

@NightOwl888
Copy link
Contributor Author

@eladmarg

Not sure. Being that ConditionalWeakTable requires the key and values to be dereferenced before it will clean them up, this poses a problem to be able to check whether they actually still exist (because they cannot be looked up without those references). So, maybe the only way to check is to profile/benchmark the app to see if it makes a difference, but other suggestions are welcome.

However, testing on .NET Framework seems to have settled at around 1-2 min longer than .NET 5, where before it varied and was for the most part around 14 minutes instead of around 11-12 minutes.

…ParentReadersEvent and GetCacheKeysEvent declarations to decouple references between consumers.
…heKeysEvent to avoid the need for a finalizer on CachedOrds and additional event declaration
…ent to avoid the need for a finalizer on DocIdSet and an additional event declaration
…gistered multiple times, and will track the registrations to clean them up during Dispose()
@NightOwl888
Copy link
Contributor Author

I have altered this a bit:

  1. The events were migrated to a new Lucene.Net.Util.Events internal static class.
  2. Removed the GetRamBytesUsedEvent and GetDocIdSetsEvent and reused the GetCacheKeysEvent in these cases, looking up the values from the ConditionalWeakTable based on the keys. While this makes the event functionality a bit slower, it avoids putting DocIdSet and CachedOrds objects into the finalizer queue and shifts the responsibility of cleaning up events to IndexReader.Dispose(). This method is almost certainly guaranteed to be called by the consumer, which takes it out of the finalizer queue.
  3. Changed the event tracking to allow multiple instances of the same event to be attached to an IndexReader instance, which solves any potential limitations if the same IndexReader instance is involved in 2 or more different contexts that require events.

So, this basically avoids any finalizer calls to enable the weak event functionality.

But even if that were not so, avoiding the huge overhead of re-allocating a Dictionary repeatedly by relying only on ConditionalWeakTable and weak events is certainly more efficient than WeakDictionary.

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.

Port ConditionalWeakTable from .NET Core 3.x to .NET Standard 2.0
4 participants