[
https://issues.apache.org/jira/browse/CASSANDRA-5062?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13626574#comment-13626574
]
Sylvain Lebresne commented on CASSANDRA-5062:
---------------------------------------------
bq. I don't think PaxosState introduces artificial contention in the sense that
we do need to serialize paxos operations, so this is definitely better than One
Big Lock
Sorry that wasn't clear, I was think of contention between proposers, not
contention local to a replica. Because we bucket the paxos states, we will
reuse the same state for different keys. So in other words, the current
algorithm does not do Paxos at the row level, but rather paxos at the level of
all rows whose key's hashcode modulo 1024 is equal, and 2 proposers on 2
different row keys may compete with each other.
And since proposer contention is so damn expensive, I wonder if that artificial
contention won't trump any gain we have from keeping the paxos state in memory
(in cases where people have a non-trivial load of CAS operation, even though it
is on different row keys). Sure we could play with the STATE_BUCKETS value,
but I'm still of the opinion that it's premature optimization at this point.
On the part about serializing Paxos operations on a given replica, I agree with
you that it's necessary, and for that we could absolutely use lock buckets
(like we use to do for 2ndary indexes) to optimize. But we can do that without
bucketing the whole paxos states.
As a side note, I think that we may want to TTL the Paxos state (we do store
the MRC update after all, if someone has done an CAS on a row and don't do any
CAS anymore, it would be sad to keep that MRC forever). And we can use a
setting like gc_grace, since provided you do guarantee that everything older
than gc_grace is consistent, then you can forget about older than gc_grace
states. Of course, in practice a setting separated from gc_grace would make
sense.
bq. Would rather make them follow-on tickets
I'm fine with that. Just wanted to mention it cause I don't think we should
release CAS without those tickets done. Maybe we can create a top-level ticket
to group all those tickets, making sure we don't forget something important?
bq. We wait for as many nodes as were alive at the start of the paxos request.
Fair enough, I misread that part. *But*, this in turn means that we'll timeout
as soon as any node that we though were alive happens to be dead, even if we
have a QUORUM of responses. And since the failure detector takes in general at
least a handful of seconds to detect a node, it means that each time a node
dies, *all* CAS operations will timeout until at least one other node detects
it dead (and while it's true that currently some reads may timeout when a node
dies, this is at least not all reads and besides, it's known to be bad for
latency and one of the reason why CASSANDRA-4705 is useful). Overall I think we
should make progress as soon as we have QUORUM answers.
Furthermore, another problem is that we throw an exception if we haven't a
quorum on the last MRC. But that means in turns that if amongst all live nodes
we don't have a QUORUM of nodes that have learn that last MRC, we don't do
anything to fix the issue (we throw an exception in fact). So if we have really
lost messages, until something external magically repair the nodes that are
missing the last MRC, we'll be in a state when no CAS operation on the row can
proceed at all. Even believing that hinted handoff will be that external
repair, which would assume that hinted handoffs are bullet-proof (and they are
not), there could still be a reasonably long window of unavailability just
because HH delivery is not prioritized/timely.
Lastly, I note that it's not unlikely not to get a QUORUM of node on the last
MRC due to timing, because the last MRC commit may have reach some replica but
not all of them (it will even likely be common when proposers contend, because
the one that gets to be 2nd will do it's prepare phase more or less at the same
time the previous one does his commit). And in that case, note that nothing is
wrong with the cluster per se, all replica may be alive. And thus throwing an
UnavailableException in that case would imo be a very unfriendly user
experience.
So to recall, what I suggest instead would be to 1) only wait for QUORUM
responses on prepare then 2) if not everyone is on the same last MRC, then
"repair" those are aren't by committing said last MRC (with the MRC ballot in
particular, not our ballot. This is not part of the Paxos algorithm per-se,
this is just us re-sending the learn message of the previous round to make sure
a quorum has it) and then 3) restart from the beginning (i.e. re-prepare).
I believe this does not suffer from the problems above: we prepare, and if the
first quorum we get hasn't got the last MRC yet (which can be due to timing or
because of some lost message, we don't know but shouldn't assume either one),
we sent it to them and we start over again.
Then we can probably optimize a bit, since we know when we "repair" the replica
that we're going to re-prepare just afterwards, we could do both in one
message. This does complicate the logic a bit though and since that's just an
optimization, this could clearly be moved to a follow-up ticket.
bq. We don't erase the promise, and we shouldn't, because that would allow us
to accept an out-of-date propose
I don't think that matters because we have the MRC to avoid accepting
out-of-date proposal (and the logic already does it). In all fairness, keeping
the promise is not un-correct either, it's just that I think it's useless to
keep around.
bq. Agreed, but the logic to do this gets kind of ugly
Can't we just say in PrepareCallback that if promised != true for a response,
then we do a 'while (latch.getCount() > 0) latch.countDown()'? Without
pretending this is the most elegant code ever, this seems simple enough to be
worth it to me.
bq. What does this buy us?
I'd say correctness :D. I we bother with Paxos to serialize our commits, I
think we want to guarantee that the column timestamp resolution won't break
that order. If I CAS more or less simultaneously 0->1 and 1->2, then if the 2nd
one return true, it means it has been serialized after the first one, so I
should be guaranteed to not get 1 when I read, but I could if due to clock skew
or other the value 2 gets a timestamp lower than 1.
bq. I prefer to keep ballot/proposal separate except for the MRC since
promise/accept are done at different times
My reasoning is that the high-level view of the algorithm is: you prepare with
some commmit ballot to see the state of the algorithm, and then you propose a
commit, and commit it if it's accepted. So I still think using Commit for
propose and commit would be cleaner and merge Commit and ProposeRequest which
are really the same. Anyway, it's a detail I suppose.
> Support CAS
> -----------
>
> Key: CASSANDRA-5062
> URL: https://issues.apache.org/jira/browse/CASSANDRA-5062
> Project: Cassandra
> Issue Type: New Feature
> Components: API, Core
> Reporter: Jonathan Ellis
> Fix For: 2.0
>
> Attachments: half-baked commit 1.jpg, half-baked commit 2.jpg,
> half-baked commit 3.jpg
>
>
> "Strong" consistency is not enough to prevent race conditions. The classic
> example is user account creation: we want to ensure usernames are unique, so
> we only want to signal account creation success if nobody else has created
> the account yet. But naive read-then-write allows clients to race and both
> think they have a green light to create.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira