Josh McKenzie created CASSANDRA-15362:
-----------------------------------------
Summary: Add a resumable commit log reader for CDC consumption
Key: CASSANDRA-15362
URL: https://issues.apache.org/jira/browse/CASSANDRA-15362
Project: Cassandra
Issue Type: New Feature
Components: Feature/Change Data Capture
Reporter: Josh McKenzie
Assignee: Josh McKenzie
[Link to wip
branch|https://github.com/apache/cassandra/compare/trunk...josh-mckenzie:resumable_clreader]
This ticket builds upon and supersedes CASSANDRA-15196.
One of the initial shortcomings of CDC after its initial implementation is
that, upon update of the related CDC index file if parsing an active segment, a
user needs to re-scan files (specifically compressed or encrypted) as they're
written and discard duplicate mutations. The primary push of this patch is to
introduce a new class, the
[ResumableCommitLogReader|https://github.com/josh-mckenzie/cassandra/blob/resumable_clreader/src/java/org/apache/cassandra/db/commitlog/ResumableCommitLogReader.java#L35-L53]
that greatly simplifies and optimizes repeated reads from a commitlog file
being actively written underneath you.
The primary API consists of:
{noformat}
public void readPartial(int readLimit) throws IOException;
public void readToCompletion() throws IOException
public void close(); // is AutoCloseable
{noformat}
An example of the usage of this can be seen in the changed standard read
implementation within the
[CommitLogReader|https://github.com/josh-mckenzie/cassandra/blob/resumable_clreader/src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java#L129-L134]
The simple "read straight through" case:
{noformat}
try(ResumableCommitLogReader resumableReader = new
ResumableCommitLogReader(file, handler, minPosition, mutationLimit,
tolerateTruncation))
{
resumableReader.readToCompletion();
}
{noformat}
The more complex "resume reading" case:
{noformat}
try(ResumableCommitLogReader resumableReader = new
ResumableCommitLogReader(file, handler, minPosition, mutationLimit,
tolerateTruncation))
{
while(!parseCDCCompletionStatus(cdcIndexFile))
{
Thread.sleep(10000);
rr.readPartial(parseCDCOffset(cdcIndexFile));
}
}
{noformat}
A brief enumeration of some of the most obvious pros and cons of this current
design and implementation:
Pros:
* Very simple external user interface
* All the complexity of resuming, RAR seeking, RAR re-creation hidden from
end-users
* Minimal risk to existing CL reading code (isPartial() path very separate
from default in computeNext())
Cons:
* Creation of a resumable reader object required on each new segment file read
(trivially fixable if a concern)
* Added complexity burden on computeNext of snapshotting Segmenter state and
reverting:
[reference|https://github.com/josh-mckenzie/cassandra/blob/resumable_clreader/src/java/org/apache/cassandra/db/commitlog/CommitLogSegmentReader.java#L92]
* Added complexity burden on computeNext of Sentinel state and resumability of
iterator
* Generally breaking the "one way street" paradigm of iterators (biggest
concern to me atm)
* Philosophical "add complexity to C* code-base to ease 'external tool' users"
debate
One other meta trade-off: this design puts the ResumableCommitLogReader at the
center of the Commit log reading design; most other related classes now take
that as an argument and it serves as both our parent logic coordinator + data
holder. The obvious benefit of this design is it's a lot easier to pass things
around and you have to grok really one thing to know what data and context you
have to work with. The obvious downside is risk of calcification and lack of
modularity plus the need to construct on any read. It's trivial to unwind that
and keep CommitLogReader et al totally unaware of the resumable reader if we're
so inclined.
Anyway - figured I'd toss this ticket up here so it's visible to people that
some of this is in flight. I've spoken with several engineers from different
companies that are using CDC at scale and have backported things to 3.0 or 3.11
(CASSANDRA-12148 for instance), so having this code here visible may be of use
to someone even in it's not quite yet polished or tested state.
There are some obvious counter-designs (add a skipToNextSyncMarker call to
Segmenter for example, or break out of iterator paradigm entirely and have an
API for {{computeNextSegment}} or {{revertToLastKnownSegment}} and just walk
across SyncSegments internal to a non-iterator based class), etc. I prefer not
to rewrite things _too_ much when working on them (and we already have some
complexity in {{SegmentReader.computeNext}} coming into this), and I'm getting
more partial to the "keep things as stupidly simple for external users as
possible" approach to APIs as I age so that's why my initial cut at this takes
on the burden of this complexity internally to keep the consumption braindead
easy.
Currently passes CommitLogReader + new ResumableCommitLogReader tests; haven't
run it through the entire gamut yet. Figured it'd be worth kicking it out here
to see if anyone has feedback that significantly alters the design before going
into the full gauntlet of tests.
--
This message was sent by Atlassian Jira
(v8.3.4#803005)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]