-
Notifications
You must be signed in to change notification settings - Fork 618
HDDS-12742. Make RDBStoreAbstractIterator Return Reference-Counted KeyValues #8203
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
Changes from 15 commits
30facb9
aab6ebc
5dd7460
e4bf8f7
9ab91c9
abe06ab
d215d4b
85c74b4
7d82a9e
ce6ab81
091e8ca
6bc5c86
1b394c2
7fdced2
f5b633b
1971a96
a33f265
0c3ae4f
fbe213b
71f9c28
5734027
99c508e
f16e1b9
86fe101
cfde81f
f825754
e4155a6
985d612
b98968b
78d33af
ed632a8
58f81cc
3f77cbe
1fecc85
deb0011
346a685
f6b651c
93c7f91
f81c116
7b37a29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,8 @@ | |
| import java.util.NoSuchElementException; | ||
| import java.util.function.Consumer; | ||
| import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksIterator; | ||
| import org.apache.ratis.util.ReferenceCountedObject; | ||
| import org.apache.ratis.util.function.UncheckedAutoCloseableSupplier; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
|
|
@@ -30,30 +32,34 @@ | |
| * @param <RAW> the raw type. | ||
| */ | ||
| abstract class RDBStoreAbstractIterator<RAW> | ||
| implements TableIterator<RAW, Table.KeyValue<RAW, RAW>> { | ||
| implements TableIterator<RAW, UncheckedAutoCloseableSupplier<RawKeyValue<RAW>>> { | ||
|
|
||
| private static final Logger LOG = | ||
| LoggerFactory.getLogger(RDBStoreAbstractIterator.class); | ||
|
|
||
| private final ManagedRocksIterator rocksDBIterator; | ||
| private final RDBTable rocksDBTable; | ||
| private Table.KeyValue<RAW, RAW> currentEntry; | ||
| private ReferenceCountedObject<RawKeyValue<RAW>> currentEntry; | ||
| private RawKeyValue<RAW> previousKeyValue; | ||
| // This is for schemas that use a fixed-length | ||
| // prefix for each key. | ||
| private final RAW prefix; | ||
| private boolean hasNext; | ||
| private boolean closed; | ||
|
|
||
| RDBStoreAbstractIterator(ManagedRocksIterator iterator, RDBTable table, | ||
| RAW prefix) { | ||
| this.rocksDBIterator = iterator; | ||
| this.rocksDBTable = table; | ||
| this.prefix = prefix; | ||
| this.currentEntry = null; | ||
| this.hasNext = false; | ||
| this.closed = false; | ||
| this.previousKeyValue = null; | ||
| } | ||
|
|
||
| /** @return the key for the current entry. */ | ||
| abstract RAW key(); | ||
|
|
||
| /** @return the {@link Table.KeyValue} for the current entry. */ | ||
| abstract Table.KeyValue<RAW, RAW> getKeyValue(); | ||
| abstract ReferenceCountedObject<RawKeyValue<RAW>> getKeyValue(); | ||
|
|
||
| /** Seek to the given key. */ | ||
| abstract void seek0(RAW key); | ||
|
|
@@ -78,32 +84,45 @@ final RAW getPrefix() { | |
|
|
||
| @Override | ||
| public final void forEachRemaining( | ||
| Consumer<? super Table.KeyValue<RAW, RAW>> action) { | ||
| Consumer<? super UncheckedAutoCloseableSupplier<RawKeyValue<RAW>>> action) { | ||
| while (hasNext()) { | ||
| action.accept(next()); | ||
| UncheckedAutoCloseableSupplier<RawKeyValue<RAW>> entry = next(); | ||
| action.accept(entry); | ||
| } | ||
| } | ||
|
|
||
| private void setCurrentEntry() { | ||
| if (rocksDBIterator.get().isValid()) { | ||
| if (currentEntry != null) { | ||
| currentEntry.release(); | ||
| } | ||
|
|
||
| boolean isValid = !closed && rocksDBIterator.get().isValid(); | ||
| if (isValid) { | ||
| currentEntry = getKeyValue(); | ||
| currentEntry.retain(); | ||
| } else { | ||
| currentEntry = null; | ||
| } | ||
| setHasNext(isValid, currentEntry); | ||
|
sumitagrawl marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| public void setHasNext(boolean isValid, ReferenceCountedObject<RawKeyValue<RAW>> entry) { | ||
| this.hasNext = isValid && (prefix == null || startsWithPrefix(entry.get().getKey())); | ||
| } | ||
|
|
||
| @Override | ||
| public final boolean hasNext() { | ||
| return rocksDBIterator.get().isValid() && | ||
| (prefix == null || startsWithPrefix(key())); | ||
| return hasNext; | ||
| } | ||
|
|
||
| @Override | ||
| public final Table.KeyValue<RAW, RAW> next() { | ||
| setCurrentEntry(); | ||
| if (currentEntry != null) { | ||
| public final UncheckedAutoCloseableSupplier<RawKeyValue<RAW>> next() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @swamirishi , The current single-threaded API is below TypedTable<ContainerID, String> table = ...;
try (TableIterator<ContainerID, ? extends Table.KeyValue<ContainerID, String>> i = table.iterator()) {
final KeyValue<ContainerID, String> keyValue = i.next();
// process it
}What is the multi-threaded API in your mind? What would the above code look like?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ThreadLocal will not work. If you see the change in the algorithm, the value set here in this is for the next value in the iterator, the thread receives the prev value that is stored in the memory.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am planning to implement the java.util.SplitIterator for multi threaded api
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Above is the high level idea I have for the parallel iterator.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What is "..."? How to obtain an iterator? What are the parameters?
I guess you are not saying that all OM, recon code have to implement a SplitIterator. My question is the API using the new iterator -- how the OM, Recon code will look like?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Table interface will have splitIterator function. Just chalking out the high level idea of the interface changes in the next set of PRs
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about simply passing an executor and a boolean TypedTable<ContainerID, String> table = ...;
Executor executor = ...;
boolean ordered = true|false; // Are the returned elements preserving the original iteration order?
try (TableIterator<ContainerID, ? extends Table.KeyValue<ContainerID, String>> I
= table.iterator(executor, orderd)) {
final KeyValue<ContainerID, String> keyValue = i.next();
// process it as before
}The OM, Recon code should not care about the implemetation details such as how the buffers gets released.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah the typed table interface will translate the consumer operation |
||
| if (hasNext()) { | ||
| UncheckedAutoCloseableSupplier<RawKeyValue<RAW>> entry = currentEntry.retainAndReleaseOnClose(); | ||
| this.previousKeyValue = entry.get(); | ||
| rocksDBIterator.get().next(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rocksDbIterator.get().next() can also be moved to setCurrentEntry(), as we are getting next entry, updating hasNext flag together.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no we cannot do that. What about for the first entry. You shouldn't call next for the first entry in the iterator |
||
| return currentEntry; | ||
| setCurrentEntry(); | ||
| return entry; | ||
| } | ||
| throw new NoSuchElementException("RocksDB Store has no more elements"); | ||
| } | ||
|
|
@@ -129,19 +148,23 @@ public final void seekToLast() { | |
| } | ||
|
|
||
| @Override | ||
| public final Table.KeyValue<RAW, RAW> seek(RAW key) { | ||
| public final UncheckedAutoCloseableSupplier<RawKeyValue<RAW>> seek(RAW key) { | ||
| seek0(key); | ||
| setCurrentEntry(); | ||
| return currentEntry; | ||
| // Current entry should be only closed when the next() and thus closing the returned entry should be a noop. | ||
| if (hasNext()) { | ||
| return currentEntry.retainAndReleaseOnClose(); | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| public final void removeFromDB() throws IOException { | ||
| if (rocksDBTable == null) { | ||
| throw new UnsupportedOperationException("remove"); | ||
| } | ||
| if (currentEntry != null) { | ||
| delete(currentEntry.getKey()); | ||
| if (previousKeyValue != null) { | ||
|
sumitagrawl marked this conversation as resolved.
Outdated
|
||
| delete(previousKeyValue.getKey()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since being reference counted, if after release, this is called can have issue. Need keep reference Counted only, and throw exception if its closed
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RemoveFromDB should not be called after release. That is the whole point of returning a CloseableValue. Even getKey() and getValue() won't be valid. |
||
| } else { | ||
| LOG.info("Failed to removeFromDB: currentEntry == null"); | ||
| } | ||
|
|
@@ -150,5 +173,7 @@ public final void removeFromDB() throws IOException { | |
| @Override | ||
| public void close() { | ||
| rocksDBIterator.close(); | ||
| closed = true; | ||
| setCurrentEntry(); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.