-
Notifications
You must be signed in to change notification settings - Fork 593
HDDS-10076. SnapshotCache closes RocksDB instance with Reference. #5934
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
Conversation
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotCache.java
Outdated
Show resolved
Hide resolved
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotCache.java
Show resolved
Hide resolved
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotCache.java
Show resolved
Hide resolved
smengcl
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @aswinshakil . The fix lgtm. Some nits inline.
hemantk-12
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @aswinshakil for the patch.
Overall looks good to me. Left a inline comment, please check that.
| } catch (IOException ex) { | ||
| throw new IllegalStateException("Error while closing snapshot DB", ex); | ||
| } | ||
| dbMap.computeIfPresent(key, (k, v) -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that we should use compute and log/throw an error if dbMap doesn't contain the key. In case when key is missing from dbMap, it is possible that object was not closed properly or some other inconsistency issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out. Updated the patch.
swamirishi
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aswinshakil Thanks for the quick patch and for identifying the issue. I am not sure if this patch completely fixes the race condition, would like other reviewers as well to double check the same.
| + ", actual: " + v + " for key: " + key); | ||
|
|
||
| // Close the instance, which also closes its DB handle. | ||
| if (rcOmSnapshot.getTotalRefCount() == 0L) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This particular check is not exactly thread safe there would still be a race condition, this doesn't really take a ref count lock. We should probably put this function check inside ReferenceCounted class which would take a ref count lock when doing the check. Some function like
boolean isTotalRefCountEqual(long expectedValue) which would return if the value is equal by taking a ref count lock. You can refer incrementRefCount function for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetKey increaments the ref count after the dbMap.compute() function so the lock is already out of scope. I don't see any change in the get function so this should be the way going forward for the cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a need to get refCountLock unless you are actually updating it. By that logic then we would also need to get a refCountLock everytime we do refCount.get(). @smengcl Do you see any problem with this? AFAIK I don't think this is problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah every time we do a refCount.get() if it is going to impact the cache yes we need to take a lock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess separating out the referenceCounted and cacheLoader is not the right way to implement the cache. From what I know this has go hand in hand in a single lock. Increasing the reference count or decreasing the reference count or when evicting an instance from the cache which would check if there are any references. BTW even decrementRefCount has this problem. decrement is called with referenceCountLock, and it checks if the reference count is zero and adds it to the pending eviction list which checks the count is 0. But in case of a race condition a get call could have a closed rocksdb instance, cleaner thread could have closed the instance in the background.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refCount increment should happen when things are loaded in the cache and within the same lock. i.e. the same db.compute method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refCount increment cannot happen in the same db.compute() because it will cause a deadlock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about a new atomic operation like rcOmSnapshot.closeObjSafely(). It only closes the internal obj if refcount == 0, and it returns true when the internal object is successfully closed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it comes to the same point when we call rcOmSnapshot.closeObjSafely() it returns true but when actually closing it, object is referenced again.
|
Closing it as #5986 fixes this issue. |
What changes were proposed in this pull request?
While accessing snapshots, We use
SnapshotCacheto load and retrieve a snapshot's RocksDB instance. The function call is as followsget()->cleanup(), When multiple background process callsget()some threads can also be doing thecleanup()of pending eviction list.There is a scenario, where Thread 1(KeyDeletingService) is executing
get()->cleanup()method and Thread 2(SSTFilteringService) is executingget()(Hasn't reachedcleanup()yet), The reference count of the snapshot is incremented byget()(Thread 2) but we still close the rocksDB instance because thecleanup()(Thread1) method assumes everything in the pending eviction list has a reference count of 0. This is not the case in the above-mentioned scenario, We need to recheck if the reference count is still 0 when closing the RocksDB instance.What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-10076
How was this patch tested?
Manually tested with an existing test.