Thread: pgbench vs. SERIALIZABLE

pgbench vs. SERIALIZABLE

From
Josh Berkus
Date:
Hackers,

I recently had a reason to benchmark a database which is default
SERIALIZABLE mode.  I was startled to discover that pgbench is set up to
abort the client once it hits a serialization failure.  You get a bunch
of these:

Client 7 aborted in state 11: ERROR:  could not serialize access due to
read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during write.
HINT:  The transaction might succeed if retried.
Client 0 aborted in state 11: ERROR:  could not serialize access due to
read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during write.

... which continue until you're down to one client, which then finished
out the pgbench (at very low rates, of course).

The problem is this code here:
               if (commands[st->state]->type == SQL_COMMAND)               {                       /*
                        * Read and discard the query result; note this
is not included in                        * the statement latency numbers.
                        */                       res = PQgetResult(st->con);                       switch
(PQresultStatus(res))                      {                               case PGRES_COMMAND_OK:
       case PGRES_TUPLES_OK:                                       break;          /* OK */
 default:                                       fprintf(stderr, "Client %d
 
aborted in state %d: %s",                                                       st->id,
st->state, PQerrorMessage(st->con));                                       PQclear(res);
      return clientDone(st, false);                       }                       PQclear(res);
discard_response(st);


The way I read that, if the client encounters any errors at all, it
gives up and halts that client.  This doesn't seem very robust, and it
certainly won't work with SERIALIZABLE.

My thinking is that what pgbench should do is:
* track an error count
* if it finds an error, don't increment the transaction count, but do
increment the error count.
* then continue to the next transaction.

Does that seem like the right approach?

-- 
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com



Re: pgbench vs. SERIALIZABLE

From
Fabien COELHO
Date:
> My thinking is that what pgbench should do is:
> * track an error count
> * if it finds an error, don't increment the transaction count, but do
> increment the error count.
> * then continue to the next transaction.
>
> Does that seem like the right approach?

Could it retry the *same* transaction instead of the next one?

Should it give up trying under some conditions, say there are more errors 
than transactions?

-- 
Fabien.



Re: pgbench vs. SERIALIZABLE

From
Marko Tiikkaja
Date:
On 2013-05-19 00:03, Fabien COELHO wrote:
>> My thinking is that what pgbench should do is:
>> * track an error count
>> * if it finds an error, don't increment the transaction count, but do
>> increment the error count.
>> * then continue to the next transaction.
>>
>> Does that seem like the right approach?
>
> Should it give up trying under some conditions, say there are more errors
> than transactions?

I don't really see the point of that.  I can't think of a scenario where 
you would get too many serialization errors to even finish the pgbench test.

At any rate, as proposed, this would fail horribly if the very first 
transaction fails, or the second transaction fails twice, etc..


Regards,
Marko Tiikkaja



Re: pgbench vs. SERIALIZABLE

From
Fabien COELHO
Date:
>> Should it give up trying under some conditions, say there are more errors
>> than transactions?
>
> I don't really see the point of that.  I can't think of a scenario where you 
> would get too many serialization errors to even finish the pgbench test.

My point is really to avoid in principle a potential infinite loop under 
option -t in these conditions, if all transactions are failing because of 
a table lock for instance. If pgbench is just killed, I'm not sure you get 
a report.

> At any rate, as proposed, this would fail horribly if the very first 
> transaction fails, or the second transaction fails twice, etc..

Yep. Or maybe some more options to control the expected behavior on 
transaction failures ? --stop-client-on-fail (current behavior), 
--keep-trying-indefinitely, --stop-client-after=<nfails>... or nothing if 
this is not a problem:-)

-- 
Fabien.



Re: pgbench vs. SERIALIZABLE

From
Kevin Grittner
Date:
Josh Berkus <josh@agliodbs.com> wrote:

> I recently had a reason to benchmark a database which is default
> SERIALIZABLE mode.  I was startled to discover that pgbench is set up to
> abort the client once it hits a serialization failure.  You get a bunch
> of these:
>
> Client 7 aborted in state 11: ERROR:  could not serialize access due to
> read/write dependencies among transactions
> DETAIL:  Reason code: Canceled on identification as a pivot, during write.
> HINT:  The transaction might succeed if retried.
> Client 0 aborted in state 11: ERROR:  could not serialize access due to
> read/write dependencies among transactions
> DETAIL:  Reason code: Canceled on identification as a pivot, during write.
>
> ... which continue until you're down to one client, which then finished
> out the pgbench (at very low rates, of course).
>
> The problem is this code here:
>
>                 if (commands[st->state]->type == SQL_COMMAND)
>                 {
>                         /*
>
>                         * Read and discard the query result; note this
> is not included in
>                         * the statement latency numbers.
>
>                         */
>                         res = PQgetResult(st->con);
>                         switch (PQresultStatus(res))
>                         {
>                                 case PGRES_COMMAND_OK:
>                                 case PGRES_TUPLES_OK:
>                                         break;          /* OK */
>                                 default:
>                                         fprintf(stderr, "Client %d
> aborted in state %d: %s",
>                                                         st->id,
> st->state, PQerrorMessage(st->con));
>                                         PQclear(res);
>                                         return clientDone(st, false);
>                         }
>                         PQclear(res);
>                         discard_response(st);
>
>
> The way I read that, if the client encounters any errors at all, it
> gives up and halts that client.  This doesn't seem very robust, and it
> certainly won't work with SERIALIZABLE.

Yes, I ran into this and wound up testing with a hacked copy of
pgbench. Anyone using SERIALIZABLE transactions needs to be
prepared to handle serialization failures.  Even REPEATABLE READ
can see a lot of serialization failures due to write conflicts in
some workloads, and READ COMMITTED can see deadlocks on certain
workloads.

> My thinking is that what pgbench should do is:
> * track an error count
> * if it finds an error, don't increment the transaction count, but do
> increment the error count.
> * then continue to the next transaction.
>
> Does that seem like the right approach?

The main thing is to not consider a transaction complete on a
serialization failure.  Many frameworks will retry a transaction
from the start on a serialization failure.  pgbench should do the
same.  The transaction should be rolled back and retried without
incrementing the transaction count.  It would not hurt to keep
track of the retries, but as long as the transaction is retried
until successful you will get meaningful throughput numbers.

Perhaps other errors should also be counted and handled better.
For SQLSTATE values *other than* 40001 and 40P01 we should not
retry the same transaction, though, because it could well be a case
where the same attempt will head-bang forever.  Serialization
failures, by definition, can be expected to work on retry.  (Not
always on the *first* retry, but any benchmark tool should keep at
it until the transaction succeeds or gets a hard error if you want
meaningful numbers, because that's what a software frameworks
should be doing -- and many do.)

I raised this issue near the end of SSI development, but nobody
seemed very interested and someone argued that a tool to do that
would be good but we shouldn't try to do it in pgbench -- so I let
it drop at the time.

--
Kevin Grittner
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: pgbench vs. SERIALIZABLE

From
Heikki Linnakangas
Date:
On 20.05.2013 14:50, Kevin Grittner wrote:
> I raised this issue near the end of SSI development, but nobody
> seemed very interested and someone argued that a tool to do that
> would be good but we shouldn't try to do it in pgbench -- so I let
> it drop at the time.

+1 on doing it in pgbench.

- Heikki



Re: pgbench vs. SERIALIZABLE

From
Jeff Janes
Date:
On Mon, May 20, 2013 at 4:50 AM, Kevin Grittner <kgrittn@ymail.com> wrote:
 
I raised this issue near the end of SSI development, but nobody
seemed very interested and someone argued that a tool to do that
would be good but we shouldn't try to do it in pgbench -- so I let
it drop at the time.

I think it would be good to do it in pgbench, provided it can be done fairly cleanly.

Presumably we would want to repeat all of the ordinary commands, in the file, but not any of the backslash set commands that precede any ordinary commands.  But what if backslash set commands are sprinkled between ordinary commands?

Cheers,

Jeff

Re: pgbench vs. SERIALIZABLE

From
Fabien COELHO
Date:
> I think it would be good to do it in pgbench, provided it can be done
> fairly cleanly.
>
> Presumably we would want to repeat all of the ordinary commands, in the
> file, but not any of the backslash set commands that precede any ordinary
> commands.  But what if backslash set commands are sprinkled between
> ordinary commands?

As I have spent some time in pgbench recently, ISTM that when restarting 
the transaction (reset state = 0), you want to keep track of the farthest 
state that was reached on previous attempts, so as to skip backslash 
commands for those states, and restart execute them where they were left. 
That would mean one integer for this purpose in the client structure.

-- 
Fabien



Re: pgbench vs. SERIALIZABLE

From
Josh Berkus
Date:
> Presumably we would want to repeat all of the ordinary commands, in the
> file, but not any of the backslash set commands that precede any ordinary
> commands.  But what if backslash set commands are sprinkled between
> ordinary commands?

See, this is why I had no intention of retrying.  Since people can run
custom workloads, there's no good way for us to handle the different
permutations of what kinds of scripts people might write.

-- 
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com