-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Adding Mutual exclusion logic for SAI index rebuilding and ZCS strea… #5071
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
base: trunk
Are you sure you want to change the base?
Changes from all commits
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,9 @@ | |
| import java.util.Collection; | ||
| import java.util.Comparator; | ||
| import java.util.HashSet; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.LinkedHashSet; | ||
| import java.util.Map; | ||
| import java.util.NavigableMap; | ||
| import java.util.Set; | ||
| import java.util.TreeMap; | ||
|
|
@@ -46,6 +49,8 @@ public SecondaryIndexBuilder getIndexBuildTask(ColumnFamilyStore cfs, | |
|
|
||
| assert group != null : "Index group does not exist for table " + cfs.keyspace + '.' + cfs.name; | ||
|
|
||
| // First resolve, without mutating anything, which sstables each index will rebuild. | ||
| Map<StorageAttachedIndex, Collection<SSTableReader>> targets = new LinkedHashMap<>(); | ||
| indexes.stream() | ||
| .filter((i) -> i instanceof StorageAttachedIndex) | ||
| .forEach((i) -> | ||
|
|
@@ -62,11 +67,50 @@ public SecondaryIndexBuilder getIndexBuildTask(ColumnFamilyStore cfs, | |
| .collect(Collectors.toList()); | ||
| } | ||
|
|
||
| group.dropIndexSSTables(ss, sai); | ||
|
|
||
| ss.forEach(sstable -> sstables.computeIfAbsent(sstable, ignore -> new HashSet<>()).add(sai)); | ||
| targets.put(sai, ss); | ||
| }); | ||
|
|
||
| return new StorageAttachedIndexBuilder(group, sstables, isFullRebuild, false); | ||
| // Reserve the per-sstable rebuild status for every unique target sstable BEFORE deleting any index | ||
| // components. An entire-sstable (zero-copy) stream reserves the same status when its outgoing file is | ||
| // constructed, so this ensures a rebuild cannot delete/rewrite SAI components underneath an in-flight | ||
| // stream (and vice versa the stream degrades to legacy). See CASSANDRA-21520. The returned builder owns | ||
| // these reservations and releases them when it finishes. | ||
|
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. The big design question for this patch is whether or not we have to do this reservation for the entire set of SSTables. I think we do, and it's because there is no partial full index rebuild. If we have 100 SSTables, and 2 of them are being ZCS streamed, we can't just build 98 and wait until those are done with streaming, delaying the rebuild for an arbitrary window. Similarly, we can't lazily check SSTables as we delete and rebuild them, because ZCS streaming on a particular SSTable might start immediately before we attempt to rebuild it. More tactically, we have |
||
| Set<SSTableReader> reserved = new LinkedHashSet<>(); | ||
| for (Collection<SSTableReader> ss : targets.values()) | ||
| { | ||
| for (SSTableReader sstable : ss) | ||
| { | ||
| if (reserved.contains(sstable)) | ||
| continue; | ||
| if (sstable.streamRebuildState().tryBeginRebuild()) | ||
| { | ||
| reserved.add(sstable); | ||
| } | ||
| else | ||
| { | ||
| reserved.forEach(s -> s.streamRebuildState().endRebuild()); | ||
| throw new RuntimeException(String.format( | ||
| "Cannot build SAI index on %s while entire-sstable (zero-copy) streaming is in progress.", | ||
| sstable.descriptor)); | ||
|
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. nit: If we move forward with this, it might be nice to have a full listing of the SSTables that cannot be locked for rebuild. More of an operator concern than a correctness problem. It might also be good to cap it at a certain number, like 16 or 32 so we don't have a huge error message. |
||
| } | ||
| } | ||
| } | ||
|
|
||
| // Now it is safe to drop existing components and assemble the build map. | ||
| try | ||
| { | ||
| targets.forEach((sai, ss) -> | ||
| { | ||
| group.dropIndexSSTables(ss, sai); | ||
| ss.forEach(sstable -> sstables.computeIfAbsent(sstable, ignore -> new HashSet<>()).add(sai)); | ||
| }); | ||
| } | ||
| catch (RuntimeException | Error e) | ||
| { | ||
| reserved.forEach(s -> s.streamRebuildState().endRebuild()); | ||
| throw e; | ||
| } | ||
|
|
||
| return new StorageAttachedIndexBuilder(group, sstables, isFullRebuild, false, true); | ||
| } | ||
| } | ||
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.
Would it be possible to just release the SSTables one by one as they complete? That would narrow the window where ZCS wouldn't be possible...