[
https://issues.apache.org/jira/browse/CASSANDRA-8963?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14629517#comment-14629517
]
Sylvain Lebresne commented on CASSANDRA-8963:
---------------------------------------------
Without getting into the naming debate above, I'd want to say that imo the
current {{OpOrder}} implementation/API is largely ok and I'm not a huge fan of
changing for changing.
Based on my own experience with that class (which thus could be off for other
people), my suspicion is that the 2 main things that makes {{OpOrder}} hard to
approach initially are that:
# the class javadoc does a poor job at explaining clearly/simply what the class
is about. The discussions around 'consumer and producer processing contiguous
batches' and other 'position the work is at' are, I think, too abstract. And
while the example could help, it references methods that are either
non-existent ('seal()') or barely used in the codebase ('isAfter()'), making it
less than ideal for getting an initial grasps of the class and its usage in the
code.
# the actual usages of the class are barely documented. I suspect that's the
biggest problem btw. If you look at the {{readOrdering.start()}} we do at the
beginning of a read, you have no clue why we're doing it. You then scroll to
the definition of {{readOrdering}}, but that give you no real clue on what this
is about because the small comment there is both pretty vague and outdated
({{readOrdering}} is now used for much more than protecting off-heap storage).
Your third inclination is then to go to the {{OpOrder}} class javadoc, hoping
the understanding of the class will bring you enough enlightenment to figure
out why {{readOrdering}} exists on your own. But then the first point kicks in,
at which point you'll probably give up, considering all this unintuitive.
So I would submit that just fixing those 2 things would be good enough. Now,
for the sake of not just criticising without offering anything, I could propose
as a starting point for the class javadoc something like:
{noformat}
/**
* A synchronization primitive that allows a specific operation X to wait until
all other operations started before X are completed.
*
* There is 2 sides to using this class. Given a particular {@code OpOrder
order}:
* 1) each operation for which we might want to wait the completion of should
call {@code order.start()} when they start, which assign them a group
* ({@code OpOrder.Group}), and should close that group once completed
(it's crucial for any group assigned through {@code start()} be closed,
* so an {@code OpOrder.Group} should preferably be protected by a
try-with-ressources).
* 2) an operation that wants to wait on the completion of operations
protected though 1) should issue a barrier {@code order.newBarrier()}, issue it
* {@code order.issue()} and then wait on that barrier ({@code
OpOrder.Barrier.await()}). The guarantee is then that {@code await()} will only
return
* once all the operations protected through 1) that started before the
barrier has been issued have completed (where completed means "have closed
* their assigned group object").
*
* In practice, an example use case of this is the {{readOrdering}} of
{{ColumnFamilyStore}}. It is used to protect every read operation, from
* before we start reading the memtables and sstables, to when the result is
fully read. Which then allows us to protect other operation from
* racing with reads. For instance, to drop a table, we first mark the table
dropped (guaranteeing no new read on that table can start), but
* then issue a new barrier on {{readOrdering}} and wait on it before
discarding the memtables/sstables. This guarantees us that any read that
* may be in-flight when we mark the table dropped is completed before we
delete the memtables/sstables.
*/
{noformat}
Then I'd also add a bigger comment on {{readOrdering}} declaration that sums
all the reasons it is used for, and would do the same for all other OpOrder.
API-wise, the only change I'd maybe suggest is around {{issue()}}. Ideally, I'd
remove {{issue()}} altogether, making the creation of a barrier issuing it
implicitly, because that's a bit simpler. There is only one case where we don't
issue right away and maybe we can slightly refactor the code to not need this?
But if that's not possible (or too contrived), then I'd make {{newBarrier()}}
issue implicitly, remove the references to {{issue()}} in the javadoc above,
and add a {{newUnissuedBarrier()}} method for the rare cases where we need it.
If the class is easier to apprehend then it's ok if you have to go read the
fine-prints for more advanced usages.
Anyway, even if you hate the suggestions above and want to debate naming to
death (which is cool, I'm all for naming death-match), I'd humbly suggest
delaying it post-3.0 because I really think this can wait.
> Make OpOrder more intuitive
> ---------------------------
>
> Key: CASSANDRA-8963
> URL: https://issues.apache.org/jira/browse/CASSANDRA-8963
> Project: Cassandra
> Issue Type: Improvement
> Components: Core
> Reporter: Benedict
> Assignee: Benedict
> Priority: Minor
> Fix For: 3.x
>
>
> There has been plenty of feedback about OpOrder being unintuitive. As well as
> revisiting the naming, I propose to introduce an Action object with RAII
> (AutoCloseable) protection that should be more obvious to users of the API.
> We can also then protect this by a Ref instance for use cases where the
> action lifetime is illdefined, and perhaps also introduce some checks for
> actions whose lifetimes extend beyond a sensible limit to report those where
> the object reference is retained.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)