Skip to content
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

Problem with many "failed to invoke InternalPushTxn" errors when using Serializable isolation type #877

Closed
YTN0 opened this issue May 1, 2015 · 34 comments
Assignees

Comments

@YTN0
Copy link

YTN0 commented May 1, 2015

I am testing out an app which updates the same set of keys many times. The problem is, it throws a ton of the following errors:

I0501 03:24:08.647541 21931 retry.go:101] store: Get failed; retrying in 0
W0501 03:24:08.647817 21931 dist_sender.go:536] failed to invoke InternalPushTxn: failed to push "8e3eca7d-9d5b-4960-8fb1-900385256b5c" {id=5a9c2442-a739-456f-9d61-e87ce7d15791 pri=1873872797, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1430450628.695363455,1 orig=1430450628.691514813,0 max=1430450628.941514813,0}
I0501 03:24:08.648106 21931 kv.go:119] failed InternalPushTxn: failed to push "8e3eca7d-9d5b-4960-8fb1-900385256b5c" {id=5a9c2442-a739-456f-9d61-e87ce7d15791 pri=1873872797, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1430450628.695363455,1 orig=1430450628.691514813,0 max=1430450628.941514813,0}

I changed the isolation type to Snapshot, and the app seems to work better, except for the occasional stalls.

With Serializable Isolation type, it fails miserably.

Is there some kind of queue internal to cockroachdb that might be filling up with these serializable transactions, and it isn't processing them fast enough?

@spencerkimball
Copy link
Member

Those are warning, not errors (we probably will suppress them because
they're expected, but we're still pre-alpha). Does it fail to continue?
What is the app doing? Multiple goroutines all writing to the same key
repeatedly?

On Fri, May 1, 2015 at 12:04 AM, YugeshN [email protected] wrote:

I am testing out an up which updates the same set of keys many times. The
problem is, it throws a ton of the following errors:

I0501 03:24:08.647541 21931 retry.go:101] store: Get failed; retrying in 0
W0501 03:24:08.647817 21931 dist_sender.go:536] failed to invoke
InternalPushTxn: failed to push "8e3eca7d-9d5b-4960-8fb1-900385256b5c"
{id=5a9c2442-a739-456f-9d61-e87ce7d15791 pri=1873872797, iso=SERIALIZABLE,
stat=PENDING, epo=0, ts=1430450628.695363455,1 orig=1430450628.691514813,0
max=1430450628.941514813,0}
I0501 03:24:08.648106 21931 kv.go:119] failed InternalPushTxn: failed to
push "8e3eca7d-9d5b-4960-8fb1-900385256b5c"
{id=5a9c2442-a739-456f-9d61-e87ce7d15791 pri=1873872797, iso=SERIALIZABLE,
stat=PENDING, epo=0, ts=1430450628.695363455,1 orig=1430450628.691514813,0
max=1430450628.941514813,0}

I changed the isolation type to Snapshot, and the app seems to work
better, except for the occasional stalls.

With Serializable Isolation type, it fails miserably.

Is there some kind of queue internal to cockroachdb that might be filling
up with these serializable transactions, and it isn't processing them fast
enough?


Reply to this email directly or view it on GitHub
#877.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

The cockroachdb server seems to spam them continuously, and my app eventually times out. I could increase the retry backoff timer and see how that goes.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

FYI, The way the app is structured right now, after N retries and failures, it will do a rollback.

At the moment, the app is pretty much "failing" from a client / usability perspective.

I had the same app running previously on FoundationDB, and it would sail through this section without any hiccup. I didn't even realize I had a write contention issue here until now.

I will tweak the retry / failure algorithm and try again with Serializable.

@spencerkimball
Copy link
Member

Could you share the app / test harness you're using? Or describe the basic
idea?

We've so far only been concerned with correctness and haven't gotten to the
point of working out the issues surrounding performance, backoff on
contention, etc. So bear with us... My guess from what you're describing is
contending transactions on a small set of keys are causing exponential
backoff / retry. Depending on your timeout and how many txns are trying to
access the same set of keys, you could run into problems. If this is indeed
the case, then either the underlying implementation needs to be optimized
so fewer backoffs are necessary, or we might need to do something other
than backoff, such as queueing. A description of the use case would be much
appreciated so we can reproduce the problem and look into solutions.

On Fri, May 1, 2015 at 12:16 AM, YugeshN [email protected] wrote:

FYI, The way the app is structured right now, after N retries and
failures, it will do a rollback.

At the moment, the app is pretty much "failing" from a client / usability
perspective.

I had the same app running previously on FoundationDB, and it would sail
through this section without any hiccup. I didn't even realize I had a
write contention issue here until now.

I will tweak the retry / failure algorithm and try again with Serializable.


Reply to this email directly or view it on GitHub
#877 (comment)
.

@cockroach-team
Copy link

I'm not sure how foundation DB was structured, but if they used locking,
then you wouldn't expect retries unless you had deadlock. Do you happen to
know the answer? We use optimistic concurrency, so you actually can expect
lots of retries under contention. Structuring your app to give up after a
certain number of retries under these conditions is probably not what you
want to do. Because of the exponential backoff, this might not work either,
but with Cockroach, I'd recommend a timeout as opposed to just counting
retries. As I said earlier, we might want to replace the exponential
backoff with something like queueing, or at the very least, a lower max
backoff.

On Fri, May 1, 2015 at 12:23 AM, Spencer Kimball [email protected]
wrote:

Could you share the app / test harness you're using? Or describe the basic
idea?

We've so far only been concerned with correctness and haven't gotten to the
point of working out the issues surrounding performance, backoff on
contention, etc. So bear with us... My guess from what you're describing is
contending transactions on a small set of keys are causing exponential
backoff / retry. Depending on your timeout and how many txns are trying to
access the same set of keys, you could run into problems. If this is indeed
the case, then either the underlying implementation needs to be optimized
so fewer backoffs are necessary, or we might need to do something other
than backoff, such as queueing. A description of the use case would be much
appreciated so we can reproduce the problem and look into solutions.

On Fri, May 1, 2015 at 12:16 AM, YugeshN [email protected] wrote:

FYI, The way the app is structured right now, after N retries and
failures, it will do a rollback.

At the moment, the app is pretty much "failing" from a client / usability
perspective.

I had the same app running previously on FoundationDB, and it would sail
through this section without any hiccup. I didn't even realize I had a
write contention issue here until now.

I will tweak the retry / failure algorithm and try again with
Serializable.


Reply to this email directly or view it on GitHub
<
https://github.com/cockroachdb/cockroach/issues/877#issuecomment-98040170>
.


Reply to this email directly or view it on GitHub
#877 (comment)
.

You received this message because you are subscribed to the Google Groups
"Cockroach DB" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to [email protected].
For more options, visit https://groups.google.com/d/optout.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

My app is a service that I am building which was originally running on Riak, then I switched it over to FoundationDB, and since they dropped off the radar after being purchased by Apple, I've started switching it over to Cockroachdb.

I wrote my own Java client for CockroachDB (I plan on putting the code up on Github once I am done) and have started testing the Java Client by integrating it into my service i.e. essentially swapping out foundationdb for cockroachdb.

The key space I am updating isn't very large, and I don't believe there are a significant number of updates to them.. they can get fairly "busy" over a short period of time (mostly burst, not constant).

The exponential backoff and retry mechanism is one I built into the Java client. I can tweak that. My app essentially does a rollback if the Put() fails (and it seems to always fail after N retries in the Java Client).

It could be that the retries are too often.. I can tweak the backoff algorithm to see if that improves things. The retries will only occur if the error from Cockroach says it's retryable and it's not ABORT.

With regards FoundationDB, here are some details on how they handled transactions:

https://foundationdb.com/key-value-store/documentation/developer-guide.html#transactions-in-foundationdb

http://blog.foundationdb.com/whiteboard-series-isolation

@spencerkimball
Copy link
Member

Thanks for the details. Will look into it. You're already providing a huge
amount of help by testing the system out as an early adopter. Is there any
way you could write a simplified version of what your app's doing in pseudo
code, or else maybe just log statements from your client which show ops:
namely, Put/Get/Scan(Key, EndKey, IsTxn?).

With those log statements, we can reconstruct the traffic and work on
fixing the performance issues.

Thanks again!

On Fri, May 1, 2015 at 12:45 AM, YugeshN [email protected] wrote:

My app is a service that I am building which was originally running on
Riak, then I switched it over to FoundationDB, and since they dropped off
the radar after being purchased by Apple, I've started switching it over to
Cockroachdb.

I wrote my own Java client for CockroachDB (I plan on putting the code up
on Github once I am done) and have started testing the Java Client by
integrating it into my service i.e. essentially swapping out foundationdb
for cockroachdb.

The key space I am updating isn't very large, and I don't believe there
are a significant number of updates to them.. they can get fairly "busy"
over a short period of time (mostly burst, not constant).

The exponential backoff and retry mechanism is one I built into the Java
client. I can tweak that. My app essentially does a rollback if the Put()
fails (and it seems to always fail after N retries in the Java Client).

It could be that the retries are too often.. I can tweak the backoff
algorithm to see if that improves things. The retries will only occur if
the error from Cockroach says it's retryable and it's not ABORT.

With regards FoundationDB, here are some details on how they handled
transactions:

https://foundationdb.com/key-value-store/documentation/developer-guide.html#transactions-in-foundationdb

http://blog.foundationdb.com/whiteboard-series-isolation


Reply to this email directly or view it on GitHub
#877 (comment)
.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

FYI, I double checked and it seems my client is only setup to use 6 threads, so there should only be 6 concurrent writers to the same set of keys. This doesn't seem like a lot, but there could be something else going on. It could be that my retry mechanism is exacerbating the issue with the number of retries and the specific intervals I am using.

I was planning on adding logging to the client, so this is probably a good time to do that.
I will try to gather as much information as possible.

FYI, I also my service running on Couchbase as well, but didn't stick with it for very long since the "Views" feature of couchbase is very slow and pretty inconsistent (and there is no other way to perform range searches on Couchbase).

Having run the exact same code on a number of different DBs does help set certain expectations and understand the boundaries of the various DBs.

CockroachDB seems very promising thus far, so I am eager to see it get to production status, and am happy to help debug these types of problems :-)

@spencerkimball
Copy link
Member

Well we really appreciate you pushing on it. We're literally rounding the
corner right now from abstract development to production and performance
issues.

On Fri, May 1, 2015 at 1:01 AM, YugeshN [email protected] wrote:

FYI, I double checked and it seems my client is only setup to use 6
threads, so there should only be 6 concurrent writers to the same set of
keys. This doesn't seem like a lot, but there could be something else going
on. It could be that my retry mechanism is exacerbating the issue with the
number of retries and the specific intervals I am using.

I was planning on adding logging to the client, so this is probably a good
time to do that.
I will try to gather as much information as possible.

FYI, I also my service running on Couchbase as well, but didn't stick with
it for very long since the "Views" feature of couchbase is very slow and
pretty inconsistent (and there is pretty much no other way to perform range
searches on Couchbase).

Having run the exact same code on a number of different DBs does help set
certain expectations and understand the boundaries of the various DBs.

CockroachDB seems very promising thus far, so I am pretty eager to see
CockroachDB get to production status, and am happy to help debug these
types of problems :-)


Reply to this email directly or view it on GitHub
#877 (comment)
.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

So I have been digging a little into the problem. The exact transaction that is causing the problem is one that does the following:

Starts a txn
Does a Put (which is updating a k/v pair)
Does a Delete
Does a Put
EndTransaction

Initially I had the last 2 calls i.e. the Delete and Put in a Batch, but I think the Batch exacerbated the problem, so I disabled the batch for now and just had them be standard calls.

Most of the errors / problems seem to be related to only these sets of calls. I am confused about one thing....

Can someone explain what the difference is between Retryable and transaction restart?

The Proto for error says the following:

// BACKOFF is for errors that can retried by restarting the transaction
// after an exponential backoff.
BACKOFF = 1;

// If transaction_restart is not ABORT, the error condition may be handled by
// restarting the transaction (with or without a backoff).
optional TransactionRestart transaction_restart = 4 [(gogoproto.nullable) = false];

// If retryable is true, the error condition may be transient and the failed
// operation may be retried (within the same transaction).
optional bool retryable = 2 [(gogoproto.nullable) = false];

So from the proto, it sounds like retryable is referencing the operation within the transaction i.e. whether the operation can be retried or not. Does transaction restart references the entire transaction? i.e. in this case all the operations within the transaction?

For example, in some of my calls for the Initial Put, I am getting a response which says "retryable=false" and "transaction_restart=BACKOFF". I am not sure how to interpret that.

Here is a full response I am getting back:

00:13:48.349 [PeerThread-42] - [Response]header {
error {
message: "txn \"d9c154b6-096a-454d-8cd5-cb5d84efe68e\" {id=e500f1bd-f2d1-4624-bdeb-ea51f5ed5984 pri=305356840, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1430464443.710008136,0 orig=1430464443.710008136,0 max=1430464443.960008136,0} failed to push \"7303b93f-8e73-41ef-81a9-c593c8de0492\" {id=a57390b3-bc02-45f4-8514-ef6fa6dd2d88 pri=1576596605, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1430464443.700373412,1 orig=1430464443.692846572,0 max=1430464443.942846572,0}"
retryable: false
detail {
  transaction_push {
    txn {
      name: "d9c154b6-096a-454d-8cd5-cb5d84efe68e"
      key: "98851dea-74aa-45f5-86b0-0b388b0b4f40:1fde5aa1-d04d-4f0b-a6a2-f1028f587fc3"
      id: "e500f1bd-f2d1-4624-bdeb-ea51f5ed5984"
      priority: 305356840
      isolation: SERIALIZABLE
      status: PENDING
      epoch: 0
      timestamp {
        wall_time: 1430464443710008136
        logical: 0
      }
      orig_timestamp {
        wall_time: 1430464443710008136
        logical: 0
      }
      max_timestamp {
        wall_time: 1430464443960008136
        logical: 0
      }
      certain_nodes {
      }
    }
    pushee_txn {
      name: "7303b93f-8e73-41ef-81a9-c593c8de0492"
      key: "98851dea-74aa-45f5-86b0-0b388b0b4f40:1fde5aa1-d04d-4f0b-a6a2-f1028f587fc3"
      id: "a57390b3-bc02-45f4-8514-ef6fa6dd2d88"
      priority: 1576596605
      isolation: SERIALIZABLE
      status: PENDING
      epoch: 0
      last_heartbeat {
        wall_time: 1430464443700373412
        logical: 1
      }
      timestamp {
        wall_time: 1430464443700373412
        logical: 1
      }
      orig_timestamp {
        wall_time: 1430464443692846572
        logical: 0
      }
      max_timestamp {
        wall_time: 1430464443942846572
        logical: 0
      }
      certain_nodes {
      }
    }
  }
}
transaction_restart: BACKOFF
}
timestamp {
wall_time: 1430464443710008136
logical: 0
}
txn {
name: "d9c154b6-096a-454d-8cd5-cb5d84efe68e"
key: "98851dea-74aa-45f5-86b0-0b388b0b4f40:1fde5aa1-d04d-4f0b-a6a2-f1028f587fc3"
id: "e500f1bd-f2d1-4624-bdeb-ea51f5ed5984"
priority: 2088864126
isolation: SERIALIZABLE
status: PENDING
epoch: 1
timestamp {
  wall_time: 1430464443710008136
  logical: 0
}
orig_timestamp {
  wall_time: 1430464443710008136
  logical: 0
}
max_timestamp {
  wall_time: 1430464443960008136
  logical: 0
}
certain_nodes {
}
}}

@bdarnell
Copy link
Contributor

bdarnell commented May 1, 2015

If you get transaction_restart error you must restart the transaction from the beginning, using the Transaction value that was returned in the last ResponseHeader. The API for transactions should take the form of a runTransaction method that takes a function as a parameter so you can re-run the transaction easily.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

So if I understand you correctly... I need to basically issue the Put, Delete, Put and EndTransaction again, but this time with the same Transaction from the previous header (that said transaction_restart)?

@bdarnell
Copy link
Contributor

bdarnell commented May 1, 2015

Yes. In go, the interface looks like this:

  db.RunTransaction(func(txn *Txn) error {
    if err := txn.Put(k1, v1); err != nil {
      return err
    }
    if err := txn.Delete(k2); err != nil {
      return err
    }
    if err := txn.Put(k3, v3); err != nil {
      return err
    }
  })

The function that is passed in gets re-run from the beginning when there is a restart error (and the RunTransaction method is responsible for passing the Transaction from one request to the next); you'll need to have a similar interface in your java client.

@YTN0
Copy link
Author

YTN0 commented May 1, 2015

Ok, thanks Ben. Let me tweak the code around this and see how that goes.

@YTN0
Copy link
Author

YTN0 commented May 4, 2015

So I have a test scenario which can trigger this problem. Actually, what happens is that once triggered, the CockroachDB instance just constantly spits out the "failed to invoke InternalPushTxn" errors and then eventually exits and dies with the following error (this happens a little bit after my client has completely exited and shutdown):

30778789.357931852,1 orig=1430778789.351674080,0 max=1430778789.601674318,0}
I0504 22:35:01.490679 27594 kv.go:119] failed InternalBatch: failed to push "f453d6eb-458d-47f3-adbc-2437543e9982" {id=f16d3e42-7532-4f34-9490-eb21c35448e8 pri=2103797126, iso=SERIALIZABLE, stat=PENDING, epo=1, ts=1430778789.357931852,1 orig=1430778789.351674080,0 max=1430778789.601674318,0}
I0504 22:35:01.490729 27594 retry.go:101] store: Get failed; retrying in 0
W0504 22:35:01.490806 27594 start.go:168] time limit reached, initiating hard shutdown

Now it's possible that this problem / issue is somehow related to a bug in my Java client, but I suspect that even if that were true, the DB shouldn't just die. It would be interesting to see if you can trigger the same problem with a Go or Python client

My test scenario is as follows... imagine you have a user record in the DB i.e. userId = {user info}. You also create an index for email address. Now in this test scenario, there are multiple clients attempting to change the user's email address. This requires updating the user record, as well as updating the "index" i.e. deleting the old index and creating a new index.

So to create a test scenario, create a single record e.g. 1234 = blah

In your thread class, do the following in a transaction (passing in key i.e. 1234):

// read the "user" data
byte[] value = client.get(key.getBytes());

// delete old "index".. in this test case, I didn't even create the "old" index in the DB
client.delete(value);

// create and update new "index"
value = UUID.randomUUID().toString().getBytes();                    
client.put(value, key.getBytes());

// update original 
client.put(key.getBytes(), value);

Now create and run 6 or more threads and pass it the same key (so it will invoke the same set of transactions concurrently on the same record). An interesting note... when I ran this test with 2 threads, it worked fine. >3 threads triggers the problem. 6 threads definitely causes the DB to go into a loop and then die.

FYI, I am using SERIALIZABLE isolation level.

Let me know if you trigger the same error. If not, then the problem is unique to my Java client and I will have to do more digging.

@spencerkimball
Copy link
Member

Could you send more of the log? I'll need to look earlier to see why a txn
is stuck. The contention in your test looks like it's bid the priority of a
txn up to a very high value so other txns can't preempt it. Is it possible
that one of your client threads is somehow stuck? What is the behavior of
your client threads when these problems arise? Also, how long is the test
running? I guess better to just send all the logs my way and I can answer
some of these questions myself.

The log indicates the server was shutdown, not that it died. Did you send
the process a signal?

On Mon, May 4, 2015 at 6:53 PM, YugeshN [email protected] wrote:

So I have a test scenario which can trigger this problem. Actually, what
happens is that once triggered, the CockroachDB instance just constantly
spits out the "failed to invoke InternalPushTxn" errors and then eventually
exits and dies with the following error (this happens a little bit after my
client has completely exited and shutdown):

30778789.357931852,1 orig=1430778789.351674080,0
max=1430778789.601674318,0}
I0504 22:35:01.490679 27594 kv.go:119] failed InternalBatch: failed to
push "f453d6eb-458d-47f3-adbc-2437543e9982"
{id=f16d3e42-7532-4f34-9490-eb21c35448e8 pri=2103797126, iso=SERIALIZABLE,
stat=PENDING, epo=1, ts=1430778789.357931852,1 orig=1430778789.351674080,0
max=1430778789.601674318,0}
I0504 22:35:01.490729 27594 retry.go:101] store: Get failed; retrying in 0
W0504 22:35:01.490806 27594 start.go:168] time limit reached, initiating
hard shutdown

Now it's possible that this problem / issue is somehow related to a bug in
my Java client, but I suspect that even if that were true, the DB shouldn't
just die. It would be interesting to see if you can trigger the same
problem with a Go or Python client

My test scenario is as follows... imagine you have a user record in the DB
i.e. userId = {user info}. You also create an index for email address. Now
in this test scenario, there are multiple clients attempting to change the
user's email address. This requires updating the user record, as well as
updating the "index" i.e. deleting the old index and creating a new index.

So to create a test scenario, create a single record e.g. 1234 = blah

In your thread class, do the following in a transaction (passing in key
i.e. 1234):

// read the "user" data
byte[] value = client.get(key.getBytes());

// delete old "index".. in this test case, I didn't even create the "old" index in the DB
client.delete(value);

// create and update new "index"
value = UUID.randomUUID().toString().getBytes();
client.put(value, key.getBytes());

// update original
client.put(key.getBytes(), value);

Now create and run 6 or more threads and pass it the same key (so it will
invoke the same set of transactions concurrently on the same record). An
interesting note... when I ran this test with 2 threads, it worked fine. >3
threads triggers the problem. 6 threads definitely causes the DB to go into
a loop and then die.

FYI, I am using SERIALIZABLE isolation level.

Let me know if you trigger the same error. If not, then the problem is
unique to my Java client and I will have to do more digging.


Reply to this email directly or view it on GitHub
#877 (comment)
.

@cockroach-team
Copy link

Are you sure you're not hitting CTRL-C or something like that around a
minute before the process exits? The message "time limit reached,
initialting hard shutdown" occurs when you initiate a shutdown but there
are outstanding operations that prevent the node from shutting down
gracefully within a minute.
It will be more helpful if you provide a full log from startup of the
server to the end, and maybe pass -v 5 on the command line (that will
increase log verbosity).

Best,
Tobias

On Mon, May 4, 2015 at 6:53 PM, YugeshN [email protected] wrote:

So I have a test scenario which can trigger this problem. Actually, what
happens is that once triggered, the CockroachDB instance just constantly
spits out the "failed to invoke InternalPushTxn" errors and then eventually
exits and dies with the following error (this happens a little bit after my
client has completely exited and shutdown):

30778789.357931852,1 orig=1430778789.351674080,0
max=1430778789.601674318,0}
I0504 22:35:01.490679 27594 kv.go:119] failed InternalBatch: failed to
push "f453d6eb-458d-47f3-adbc-2437543e9982"
{id=f16d3e42-7532-4f34-9490-eb21c35448e8 pri=2103797126, iso=SERIALIZABLE,
stat=PENDING, epo=1, ts=1430778789.357931852,1 orig=1430778789.351674080,0
max=1430778789.601674318,0}
I0504 22:35:01.490729 27594 retry.go:101] store: Get failed; retrying in 0
W0504 22:35:01.490806 27594 start.go:168] time limit reached, initiating
hard shutdown

Now it's possible that this problem / issue is somehow related to a bug in
my Java client, but I suspect that even if that were true, the DB shouldn't
just die. It would be interesting to see if you can trigger the same
problem with a Go or Python client

My test scenario is as follows... imagine you have a user record in the DB
i.e. userId = {user info}. You also create an index for email address. Now
in this test scenario, there are multiple clients attempting to change the
user's email address. This requires updating the user record, as well as
updating the "index" i.e. deleting the old index and creating a new index.

So to create a test scenario, create a single record e.g. 1234 = blah

In your thread class, do the following in a transaction (passing in key
i.e. 1234):

// read the "user" data
byte[] value = client.get(key.getBytes());

// delete old "index".. in this test case, I didn't even create the "old" index in the DB
client.delete(value);

// create and update new "index"
value = UUID.randomUUID().toString().getBytes();
client.put(value, key.getBytes());

// update original
client.put(key.getBytes(), value);

Now create and run 6 or more threads and pass it the same key (so it will
invoke the same set of transactions concurrently on the same record). An
interesting note... when I ran this test with 2 threads, it worked fine. >3
threads triggers the problem. 6 threads definitely causes the DB to go into
a loop and then die.

FYI, I am using SERIALIZABLE isolation level.

Let me know if you trigger the same error. If not, then the problem is
unique to my Java client and I will have to do more digging.


Reply to this email directly or view it on GitHub
#877 (comment)
.

You received this message because you are subscribed to the Google Groups
"Cockroach DB" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to [email protected].
For more options, visit https://groups.google.com/d/optout.

@YTN0
Copy link
Author

YTN0 commented May 4, 2015

Nope... I did not hit Ctrl-C or send any signal to the server. In fact, I just ran the test again and I completely exited my test client, and the server is still just running constantly and dumping the "failed to push" messages. It's been going like that for about about 3-5 minutes now with no client connected. Seems like it's stuck in some kind of loop. It's not exiting / dying this time, but it isn't stopping either.

Let me see if I can get more details on what's going on.

@cockroach-team
Copy link

Something is signaling your process--it's only way you'd see that log
message (unless you're running cockroach quit). You might put some logging
in at server/cli/start.go.

It's not so surprising to see that retry loop if what's going on is what I
suspect. At worst it should go away in 5s. How long does it last for you?

What I really need is to set up your same client locally so I can debug
these problems. You wrote a bit of pseudo code before, but could you just
send me the client source? Failing that, a slightly more complicated bit of
pseudo code would be enough to recreate.

Thanks!

On Mon, May 4, 2015 at 7:18 PM, YugeshN [email protected] wrote:

Nope... I did not hit Ctrl-C or send any signal to the server. In fact, I
just ran the test again and I completely exited my test client, and the
server is still just running constantly and dumping the "failed to push"
messages. It's been going like that for about about 3-5 minutes now with no
client connected. Seems like it's stuck in some kind of loop. It's not
exiting / dying this time, but it isn't stopping either.

Let me see if I can get more details on what's going on.


Reply to this email directly or view it on GitHub
#877 (comment)
.

You received this message because you are subscribed to the Google Groups
"Cockroach DB" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to [email protected].
For more options, visit https://groups.google.com/d/optout.

@tbg
Copy link
Member

tbg commented May 10, 2015

@spencerkimball I think this is actually reproduced nicely by cockroach-python. If you run the client test, it'll get stuck for 15s and then fail. I think what is happening is that it's synchronously trying to resolve intents, and catches an infinite loop doing that. It being the weekend and all I haven't looked at it too closely, but there's definitely something very fishy there.

Here's roughly what it looks like:

  • lay down an intent with high priority
  • try to read the key outside of the txn (with lower prio, so you can't push) once

server log floods: https://gist.github.com/tschottdorf/b5dbebe1233c7f782581

The test details are in test_client.py:test_retry_non_txn; I've looked at the latest test case (Methods.Get, data_pb2.Serializable, False /* can_push */, 1 /* exp retries */).

@tbg
Copy link
Member

tbg commented May 10, 2015

The test cases which are commented in that file are also still pathological (write/write). Haven't checked them all, but it fills the log quick and intents seem to be involved. The other cases go through fine.

@tbg
Copy link
Member

tbg commented May 11, 2015

Yeah, it looks like a case of resolving intents gone wild. We've got tests that try this sort of behaviour, but in our tests we limit retries - which means the resolves fail after, say, 2 attempts. With our (as of yet untuned) standard settings, there is no limit on retries and so it goes wild at the level of the distributed sender. We'll figure something out.

@YTN0
Copy link
Author

YTN0 commented May 11, 2015

Great to hear that you guys have potentially narrowed down the problem! I thought it was related to something specific in my code and / or my misuse of the DB so I basically began working around the issue by removing some of the write contention from my app / service. I still see occasional flurries of this error in the DB log, but have managed to reduce the number of occurrences considerably with some of the changes I have made.

@mberhault
Copy link
Contributor

This can be reproduced with examples/bank/ with as little as two concurrent writers:
./bank --db-name=http://root@localhost:10002 --num-accounts=4 --num-parallel-transfers=2

Fairly quickly, all nodes start spinning on:

W0521 08:42:40.248773    7423 dist_sender.go:567] failed to invoke InternalPushTxn: failed to push "" {id=1c3b1834-9e5b-4802-ae0e-3f6bc564eb68 pri=1585678086, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1432212154.350902081,1 orig=1432212143.234682322,0 max=1432212143.484682322,0}

When that happens, the client freezes for a while. Even once the client is stopped, it can take minutes for all nodes to quiet down again.
Running with many more concurrent writers (eg: 100, the default for the banking example) causes the nodes go for long enough that they either get killed by the kernel (OOM), exceed their multiraft event backlog, or die for other reasons.

@tbg tbg self-assigned this May 21, 2015
@tbg
Copy link
Member

tbg commented May 21, 2015

I think TestRetryNonTxn in client showcases this behaviour pretty well. There, a (non-txn, but that doesn't matter too much) Put is executed against an open intent and should catch WriteIntentErrors a bunch of times, but it never returns from its first attempt when you set infinite retries.
Though the situation in the test is somewhat different: The txn remains open until the first non-txn Put has had a chance to fail. If it never returns, that's never. But retrying the write at the source seems way more performant, so it's possible that this test is just stale. I'm looking into it.

Edit: We should rewrite TestRetryNonTxn in a way that allows porting to other platforms. It currently relies on setting system-internal RetryOptions in the test. See cockroachdb/sqlalchemy-cockroachdb#3

@tbg
Copy link
Member

tbg commented May 21, 2015

One thing that likely isn't the root cause of this but that I want to note here for further inspection is that the one-off intent resolvers sent after a successful EndTransaction by TxnCoordSender are likely to get stuck in an infinite retry loop when there are busy keys. Since we resolve synchronously, it's fine for those guys to only retry once. Unfortunately, the sender will never return from those calls until they complete successfully (since they are retried at the store level) and so to implement this we need some means of communicating that the request shouldn't be retried, e.g. a flag in the args (InternalPushTxn could inspect it and return a non-retryable error).

^- edit: that's not an issue, I muddled up resolve and push. The resolve will simply be a no-op in that case and never errors out retriably.

@tbg
Copy link
Member

tbg commented May 21, 2015

I0521 13:24:52.759828 70557 send.go:196] Node.InternalPushTxn: successful reply: header:<error:<message:"failed to push "" {id=eee6f73e-cf99-4df4-a1fa-16a9e223b78e pri=1898437915, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1432228797.755115271,2 orig=1432228796.742957592,0 max=1432228796.992957592,0}" retryable:false transaction_restart:BACKOFF detail:<transaction_push:<pushee_txn:<name:"" key:"000000000" id:"\356\346\367>\317\231M\364\241\372\026\251\342#\267\216" priority:1898437915 isolation:SERIALIZABLE status:PENDING epoch:0 last_heartbeat:<wall_time:1432228801746494783 logical:0 > timestamp:<wall_time:1432228797755115163 logical:2 > orig_timestamp:<wall_time:1432228796742957604 logical:0 > max_timestamp:<wall_time:1432228796992957604 logical:0 > certain_nodes:<> > > > > timestamp:<wall_time:1432228797755115163 logical:3 > > pushee_txn:<name:"" key:"000000000" id:"\356\346\367>\317\231M\364\241\372\026\251\342#\267\216" priority:1898437915 isolation:SERIALIZABLE status:PENDING epoch:0 last_heartbeat:<wall_time:1432228801746494783 logical:0 > timestamp:<wall_time:1432228797755115163 logical:2 > orig_timestamp:<wall_time:1432228796742957604 logical:0 > max_timestamp:<wall_time:1432228796992957604 logical:0 > certain_nodes:<> >

basically there's a transaction which is PENDING but its last heartbeat was five minutes ago. Maybe other checks return from the function and fail the push (though it succeed if a transaction record goes stale without committing).

@tbg
Copy link
Member

tbg commented May 21, 2015

maybe related to #1082: if the client doesn't retry on this error, it will leave the transaction open. So either

  • if the txn coordinator heartbeats it indefinitely, pushes to this txn will fail forever (depending on prios, etc, of course)
  • if the txn coordinator stops heartbeating, then there's still the chance that the txn is never marked as expired at pushes because the timestamp used is that from the args, which for internally-retrying transactions may or may not move forward with actual node time - i'll have to take a look.

@tbg
Copy link
Member

tbg commented May 21, 2015

store.go goes into a busy retry loop trying to resolve the intents, but keeping the timestamp in the request header unchanged. since that is what's used to validate whether the pushee txn is expired, we compare the same two numbers over and over again and control never goes back to the client.

i've hacked the code locally to use the wall time in InternalPushTxn. That correctly causes the Push to go through after it's been retried often enough to let 10s pass.

I'll look next at how to avoid that busy loop and whether the txn coordinator has a mechanism to stop heartbeating when the client disappears (edit: it does, currently set to 10s as well). With the coordinator heartbeating for 10s, and the txn timing out 10s after that, we're looking at 20s during which anything that has to do with the key remains locked.

@tbg
Copy link
Member

tbg commented May 21, 2015

the real problem though is that with two concurrent writers on two keys, they can block each other:

0521 15:22:52.024620   91761 kv.go:111] failed Batch: failed to push "" {id=f9f948bc-c7b1-4f72-9158-51644b10b7dc pri=1869564463, iso=SERIALIZABLE, stat=PENDING, epo=1, ts=1432236172.014127016,1 orig=1432236172.014127016,1 max=1432236172.014127016,1}
I0521 15:22:52.025235   91761 kv.go:111] failed Batch: failed to push "" {id=8255b679-b0df-4f63-8173-e99ceed705bd pri=2141693332, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1432236172.017025709,0 orig=1432236172.017025709,0 max=1432236172.267025948,0}

Here's the dist sender side of this:

I0521 15:23:07.020192 91559 kv.go:111] failed InternalBatch: failed to push "" {id=f9f948bc-c7b1-4f72-9158-51644b10b7dc pri=1869564463, iso=SERIALIZABLE, stat=PENDING, epo=1, ts=1
432236172.014127016,1 orig=1432236172.014127016,1 max=1432236172.014127016,1}

W0521 15:23:07.020729 91559 dist_sender.go:567] failed to invoke InternalPushTxn: failed to push "" {id=8255b679-b0df-4f63-8173-e99ceed705bd pri=2141693332, iso=SERIALIZABLE, stat=PENDING, epo=0, ts=1432236172.017025709,0 orig=1432236172.017025709,0 max=1432236172.267025948,0}

It looks like the pusher (triggered by a transactional Get call) isn't transactional here (not shown in log). That's a bug, I'm going to track it down.

@tbg
Copy link
Member

tbg commented May 21, 2015

PR coming up. It's going to be embarrassingly small.

tbg added a commit to tbg/cockroach that referenced this issue May 21, 2015
previously args.Timestamp was used, but there are situations
in which that never changes, meaning that infinite retry loops
on an abandoned Txn descriptor would ensue in storage/store,
for instance in cockroachdb#877 and the bank example.
@spencerkimball
Copy link
Member

Are you sure it's only two transactions blocking each other, or one transaction which is blocking two others? I don't see how two transactions could block each other. the one with the higher priority would succeed in pushing.

tbg added a commit to tbg/cockroach that referenced this issue May 26, 2015
previously args.Timestamp was used, but there are situations
in which that never changes, meaning that infinite retry loops
on an abandoned Txn descriptor would ensue in storage/store,
for instance in cockroachdb#877 and the bank example.
@tbg
Copy link
Member

tbg commented May 26, 2015

In the bugs that I've been seeing the errors are infinite retries on the store level, where nothing really changes between attempts. The bug was that the Push would actually forget the Txn. I'm going to play with this more.

@tbg
Copy link
Member

tbg commented Jun 30, 2015

I'm closing this for now; we have the basic bugs nailed down and will have plenty of testing for the txn pipeline under contention in #628.

@tbg tbg closed this as completed Jun 30, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants