Hot Standby on git
Just a note to say that Hot Standby patch is now on git repository
git://git.postgresql.org/git/users/simon/postgres
Branch name: hot_standby
The complete contents of that repository are BSD licenced contributions
to the PostgreSQL project.
Any further changes to that will be by agreement here on hackers. From
now, I will be submitting each individual change as patch-on-patch to
allow people to see and discuss them and to confirm them as open source
contributions. I request anybody else interested to do the same to allow
us to work together. All contributions welcome.
My record of agreed changes is here
http://wiki.postgresql.org/wiki/Hot_Standby#Remaining_Work_Items
You'll notice that I've already completed 8 changes (10 commits); those
are all fairly minor changes, so submitted here as a combined patch.
There are 9 pending changes, so far, none of which appear to be major
obstacles to resolve. Many thanks to Heikki for a thorough review which
has identified nearly all of those change requests.
I estimate that making the remaining changes noted on the Wiki and fully
testing them will take at least 2 weeks. Gabriele Bartolini is assisting
in this area, though neither of us are able to work full time on this.
We still have ample time to complete the project in this release.
Many thanks to Magnus and Aidan for helping me resolve my git-wrestling
contest and apologies for the delay while that bout happened.
--
Simon Riggs www.2ndQuadrant.com
Attachments:
hs.53c1eda..13a0bd6.patchtext/x-patch; charset=UTF-8; name=hs.53c1eda..13a0bd6.patchDownload
*** a/doc/src/sgml/backup.sgml
--- b/doc/src/sgml/backup.sgml
***************
*** 1934,1941 **** if (!triggered)
</para>
<para>
! Read-only here means "no writes to the permanent database tables". So
! there are no problems with queries that make use of temporary sort and
work files will be used. Temporary tables cannot be created and
therefore cannot be used at all in recovery mode.
</para>
--- 1934,1941 ----
</para>
<para>
! Read-only here means "no writes to the permanent database tables".
! There are no problems with queries that make use of temporary sort and
work files will be used. Temporary tables cannot be created and
therefore cannot be used at all in recovery mode.
</para>
***************
*** 1983,1989 **** if (!triggered)
</listitem>
<listitem>
<para>
! LOCK, with restrictions, see later
</para>
</listitem>
<listitem>
--- 1983,1989 ----
</listitem>
<listitem>
<para>
! LOCK TABLE, though only when explicitly IN ACCESS SHARE MODE
</para>
</listitem>
<listitem>
***************
*** 2000,2014 **** if (!triggered)
</para>
<para>
! These actions will produce error messages
<itemizedlist>
<listitem>
<para>
! DML - Insert, Update, Delete, COPY FROM, Truncate which all write data.
! Any RULE which generates DML will throw error messages as a result.
! Note that there is no action possible that can result in a trigger
! being executed.
</para>
</listitem>
<listitem>
--- 2000,2013 ----
</para>
<para>
! These actions produce error messages
<itemizedlist>
<listitem>
<para>
! DML - Insert, Update, Delete, COPY FROM, Truncate.
! Note that there are no actions that result in a trigger
! being executed during recovery.
</para>
</listitem>
<listitem>
***************
*** 2024,2029 **** if (!triggered)
--- 2023,2041 ----
</listitem>
<listitem>
<para>
+ RULEs on SELECT statements that generate DML commands. RULEs on DML
+ commands that produce only SELECT statements are already disallowed
+ during read-only transactions.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ LOCK TABLE, in short default form, since it requests ACCESS EXCLUSIVE MODE.
+ LOCK TABLE that explicitly requests a lock other than ACCESS SHARE MODE.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
Transaction management commands that explicitly set non-read only state
<itemizedlist>
<listitem>
***************
*** 2069,2077 **** if (!triggered)
<para>
Note that current behaviour of read only transactions when not in
! recovery is to allow the last two actions, so there is a small and
! subtle difference in behaviour between standby read-only transactions
! and read only transactions during normal running.
It is possible that the restrictions on LISTEN, UNLISTEN, NOTIFY and
temporary tables may be lifted in a future release, if their internal
implementation is altered to make this possible.
--- 2081,2089 ----
<para>
Note that current behaviour of read only transactions when not in
! recovery is to allow the last two actions, so there are small and
! subtle differences in behaviour between read-only transactions
! run on standby and during normal running.
It is possible that the restrictions on LISTEN, UNLISTEN, NOTIFY and
temporary tables may be lifted in a future release, if their internal
implementation is altered to make this possible.
***************
*** 2082,2088 **** if (!triggered)
processing mode. Sessions will remain connected while the server
changes mode. Current transactions will continue, though will remain
read-only. After this, it will be possible to initiate read-write
! transactions, though users must *manually* reset their
default_transaction_read_only setting first, if they want that
behaviour.
</para>
--- 2094,2100 ----
processing mode. Sessions will remain connected while the server
changes mode. Current transactions will continue, though will remain
read-only. After this, it will be possible to initiate read-write
! transactions, though users must explicitly reset their
default_transaction_read_only setting first, if they want that
behaviour.
</para>
***************
*** 2098,2107 **** if (!triggered)
</para>
<para>
! In recovery, transactions will not be permitted to take any lock higher
! other than AccessShareLock or AccessExclusiveLock. In addition,
! transactions may never assign a TransactionId and may never write WAL.
! The LOCK TABLE command by default applies an AccessExclusiveLock.
Any LOCK TABLE command that runs on the standby and requests a specific
lock type other than AccessShareLock will be rejected.
</para>
--- 2110,2118 ----
</para>
<para>
! In recovery, transactions will not be permitted to take any table lock
! higher than AccessShareLock. In addition, transactions may never assign
! a TransactionId and may never write WAL.
Any LOCK TABLE command that runs on the standby and requests a specific
lock type other than AccessShareLock will be rejected.
</para>
***************
*** 2168,2175 **** if (!triggered)
<para>
An example of the above would be an Administrator on Primary server
! runs a DROP TABLE command that refers to a table currently in use by
! a User query on the standby server.
</para>
<para>
--- 2179,2186 ----
<para>
An example of the above would be an Administrator on Primary server
! runs a DROP TABLE command on a table that's currently being queried
! in the standby server.
</para>
<para>
***************
*** 2198,2206 **** if (!triggered)
<para>
We have a number of choices for resolving query conflicts. The default
is that we wait and hope the query completes. If the recovery is not paused,
! then the server will wait automatically until the server the lag between
primary and standby is at most max_standby_delay seconds. Once that grace
! period expires, we then take one of the following actions:
<itemizedlist>
<listitem>
--- 2209,2217 ----
<para>
We have a number of choices for resolving query conflicts. The default
is that we wait and hope the query completes. If the recovery is not paused,
! then the server will wait automatically until the lag between
primary and standby is at most max_standby_delay seconds. Once that grace
! period expires, we take one of the following actions:
<itemizedlist>
<listitem>
***************
*** 2213,2219 **** if (!triggered)
<para>
If the conflict is caused by cleanup records we tell the standby query
that a conflict has occurred and that it must cancel itself to avoid the
! risk that it attempts to silently fails to read relevant data because
that data has been removed. (This is very similar to the much feared
error message "snapshot too old").
</para>
--- 2224,2230 ----
<para>
If the conflict is caused by cleanup records we tell the standby query
that a conflict has occurred and that it must cancel itself to avoid the
! risk that it silently fails to read relevant data because
that data has been removed. (This is very similar to the much feared
error message "snapshot too old").
</para>
***************
*** 2222,2228 **** if (!triggered)
Note also that this means that idle-in-transaction sessions are never
canceled except by locks. Users should be clear that tables that are
regularly and heavily updated on primary server will quickly cause
! cancellation of any longer running queries made against those tables.
</para>
<para>
--- 2233,2239 ----
Note also that this means that idle-in-transaction sessions are never
canceled except by locks. Users should be clear that tables that are
regularly and heavily updated on primary server will quickly cause
! cancellation of any longer running queries in the standby.
</para>
<para>
***************
*** 2235,2241 **** if (!triggered)
</para>
<para>
! Other remdial actions exist if the number of cancelations is unacceptable.
The first option is to connect to primary server and keep a query active
for as long as we need to run queries on the standby. This guarantees that
a WAL cleanup record is never generated and we don't ever get query
--- 2246,2252 ----
</para>
<para>
! Other remedial actions exist if the number of cancelations is unacceptable.
The first option is to connect to primary server and keep a query active
for as long as we need to run queries on the standby. This guarantees that
a WAL cleanup record is never generated and we don't ever get query
***************
*** 2283,2289 **** if (!triggered)
<title>Administrator's Overview</title>
<para>
! If there is a recovery.conf file present then the will start in Hot Standby
mode by default, though this can be disabled by setting
"recovery_connections = off" in recovery.conf. The server may take some
time to enable recovery connections since the server must first complete
--- 2294,2300 ----
<title>Administrator's Overview</title>
<para>
! If there is a recovery.conf file present the server will start in Hot Standby
mode by default, though this can be disabled by setting
"recovery_connections = off" in recovery.conf. The server may take some
time to enable recovery connections since the server must first complete
***************
*** 2308,2314 **** LOG: database system is ready to accept read only connections
The setting of max_connections on the standby should be equal to or
greater than the setting of max_connections on the primary. This is to
ensure that standby has sufficient resources to manage incoming
! transactions.
</para>
<para>
--- 2319,2325 ----
The setting of max_connections on the standby should be equal to or
greater than the setting of max_connections on the primary. This is to
ensure that standby has sufficient resources to manage incoming
! transactions. max_prepared_transactions already has this restriction.
</para>
<para>
***************
*** 2329,2335 **** LOG: database system is ready to accept read only connections
A set of functions allow superusers to control the flow of recovery
are described in <xref linkend="functions-recovery-control-table">.
These functions allow you to pause and continue recovery, as well
! as dynamically set new recovery targets wile recovery progresses.
Note that when a server is paused the apparent delay between primary
and standby will continue to increase.
</para>
--- 2340,2346 ----
A set of functions allow superusers to control the flow of recovery
are described in <xref linkend="functions-recovery-control-table">.
These functions allow you to pause and continue recovery, as well
! as dynamically set new recovery targets while recovery progresses.
Note that when a server is paused the apparent delay between primary
and standby will continue to increase.
</para>
***************
*** 2342,2348 **** LOG: database system is ready to accept read only connections
themselves. Users will be able to write large sort temp files and
re-generate relcache info files, so there is no part of the database
that is truly read-only during hot standby mode. There is no restriction
! on use of set returning functions, or other users of tuplestore/tuplesort
code. Note also that writes to remote databases will still be possible,
even though the transaction is read-only locally.
</para>
--- 2353,2359 ----
themselves. Users will be able to write large sort temp files and
re-generate relcache info files, so there is no part of the database
that is truly read-only during hot standby mode. There is no restriction
! on the use of set returning functions, or other users of tuplestore/tuplesort
code. Note also that writes to remote databases will still be possible,
even though the transaction is read-only locally.
</para>
***************
*** 2354,2360 **** LOG: database system is ready to accept read only connections
</para>
<para>
! The following types of administrator command will not be accepted
during recovery mode
<itemizedlist>
--- 2365,2371 ----
</para>
<para>
! The following types of administrator command are not be accepted
during recovery mode
<itemizedlist>
***************
*** 2558,2563 **** LOG: database system is ready to accept read only connections
--- 2569,2583 ----
available for use when running queries during recovery.
</para>
</listitem>
+ <listitem>
+ <para>
+ Full knowledge of running transactions is required before snapshots
+ may be taken. Transactions that take use large numbers of subtransactions
+ (currently greater than 64) will delay the start of read only
+ connections until the completion of the longest running write transaction.
+ If this situation occurs explanatory messages will be sent to server log.
+ </para>
+ </listitem>
</itemizedlist>
</para>
*** a/src/backend/access/gin/ginxlog.c
--- b/src/backend/access/gin/ginxlog.c
***************
*** 622,628 **** gin_redo(XLogRecPtr lsn, XLogRecord *record)
uint8 info = record->xl_info & ~XLR_INFO_MASK;
/*
! * GIN indexes do not require any conflict processing. XXX really?
*/
if (InHotStandby)
RecordKnownAssignedTransactionIds(record->xl_xid);
--- 622,630 ----
uint8 info = record->xl_info & ~XLR_INFO_MASK;
/*
! * GIN indexes do not require any conflict processing. The GIN
! * posting tree is scanned in logical order during VACUUM and
! * no additional processing is required.
*/
if (InHotStandby)
RecordKnownAssignedTransactionIds(record->xl_xid);
*** a/src/backend/access/gist/gistxlog.c
--- b/src/backend/access/gist/gistxlog.c
***************
*** 397,403 **** gist_redo(XLogRecPtr lsn, XLogRecord *record)
MemoryContext oldCxt;
/*
! * GIST indexes do not require any conflict processing. XXX really?
*/
if (InHotStandby)
RecordKnownAssignedTransactionIds(record->xl_xid);
--- 397,406 ----
MemoryContext oldCxt;
/*
! * GIST indexes do not require any conflict processing. This is
! * because GIST does not remove killed tuples when it performs
! * page splits in the same way b-trees do. Also VACUUMs of
! * GIST indexes occur in logical not physical order.
*/
if (InHotStandby)
RecordKnownAssignedTransactionIds(record->xl_xid);
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 947,952 **** begin:;
--- 947,971 ----
FIN_CRC32(rdata_crc);
record->xl_crc = rdata_crc;
+ #ifdef WAL_DEBUG
+ if (XLOG_DEBUG)
+ {
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+ appendStringInfo(&buf, "INSERT @ %X/%X: ",
+ RecPtr.xlogid, RecPtr.xrecoff);
+ xlog_outrec(&buf, record);
+ if (rdata->data != NULL)
+ {
+ appendStringInfo(&buf, " - ");
+ RmgrTable[record->xl_rmid].rm_desc(&buf, record->xl_info, rdata->data);
+ }
+ elog(LOG, "%s", buf.data);
+ pfree(buf.data);
+ }
+ #endif
+
/* Record begin of record in appropriate places */
ProcLastRecPtr = RecPtr;
Insert->PrevRecord = RecPtr;
*** a/src/backend/commands/lockcmds.c
--- b/src/backend/commands/lockcmds.c
***************
*** 49,61 **** LockTableCommand(LockStmt *lockstmt)
/*
* During recovery we only accept these variations:
! *
! * LOCK TABLE foo -- implicitly, AccessExclusiveLock
! * LOCK TABLE foo IN ACCESS SHARE MODE
! * LOCK TABLE foo IN ACCESS EXCLUSIVE MODE
*/
! if (lockstmt->mode != AccessShareLock
! && lockstmt->mode != AccessExclusiveLock)
PreventCommandDuringRecovery();
LockTableRecurse(reloid, relation,
--- 49,57 ----
/*
* During recovery we only accept these variations:
! * LOCK TABLE foo IN ACCESS SHARE MODE which is effectively a no-op
*/
! if (lockstmt->mode != AccessShareLock)
PreventCommandDuringRecovery();
LockTableRecurse(reloid, relation,
*** a/src/backend/storage/ipc/procarray.c
--- b/src/backend/storage/ipc/procarray.c
***************
*** 502,509 **** ProcArrayApplyRecoveryInfo(XLogRecPtr lsn, xl_xact_running_xacts *xlrec)
if (!xlrec->subxid_overflow)
recoverySnapshotValid = true;
else
! elog(trace_recovery(DEBUG2),
! "running xact data has incomplete subtransaction data");
xids = palloc(sizeof(TransactionId) * (xlrec->xcnt + xlrec->subxcnt));
nxids = 0;
--- 502,509 ----
if (!xlrec->subxid_overflow)
recoverySnapshotValid = true;
else
! ereport(LOG,
! (errmsg("consistent state delayed because recovery snapshot incomplete")));
xids = palloc(sizeof(TransactionId) * (xlrec->xcnt + xlrec->subxcnt));
nxids = 0;
***************
*** 1502,1508 **** HaveTransactionsInCommit(TransactionId *xids, int nxids)
/*
* BackendPidGetProc -- get a backend's PGPROC given its PID
! *
* Returns NULL if not found. Note that it is up to the caller to be
* sure that the question remains meaningful for long enough for the
* answer to be used ...
--- 1502,1508 ----
/*
* BackendPidGetProc -- get a backend's PGPROC given its PID
! *
* Returns NULL if not found. Note that it is up to the caller to be
* sure that the question remains meaningful for long enough for the
* answer to be used ...
***************
*** 1536,1576 **** BackendPidGetProc(int pid)
}
/*
- * BackendXidGetProc -- get a backend's PGPROC given its XID
- *
- * Returns NULL if not found. Note that it is up to the caller to be
- * sure that the question remains meaningful for long enough for the
- * answer to be used ...
- */
- PGPROC *
- BackendXidGetProc(TransactionId xid)
- {
- PGPROC *result = NULL;
- ProcArrayStruct *arrayP = procArray;
- int index;
-
- if (xid == InvalidTransactionId) /* never match invalid xid */
- return 0;
-
- LWLockAcquire(ProcArrayLock, LW_SHARED);
-
- for (index = 0; index < arrayP->numProcs; index++)
- {
- PGPROC *proc = arrayP->procs[index];
-
- if (proc->xid == xid)
- {
- result = proc;
- break;
- }
- }
-
- LWLockRelease(ProcArrayLock);
-
- return result;
- }
-
- /*
* BackendXidGetPid -- get a backend's pid given its XID
*
* Returns 0 if not found or it's a prepared transaction. Note that
--- 1536,1541 ----
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 2695,2705 **** ProcessInterrupts(void)
* idle-in-transaction session, so make it FATAL instead.
*/
case CONFLICT_MODE_ERROR:
! cancelMode = CONFLICT_MODE_FATAL;
break;
case CONFLICT_MODE_ERROR_IF_NOT_IDLE:
! cancelMode = CONFLICT_MODE_NOT_SET;
break;
default:
--- 2695,2713 ----
* idle-in-transaction session, so make it FATAL instead.
*/
case CONFLICT_MODE_ERROR:
! cancelMode = CONFLICT_MODE_FATAL;
break;
case CONFLICT_MODE_ERROR_IF_NOT_IDLE:
! /*
! * If we still have a snapshot then we must
! * cancel, else we are free to go.
! * XXXHS: As above, cancel means FATAL, for now.
! */
! if (MyProc->xmin == 0)
! cancelMode = CONFLICT_MODE_NOT_SET;
! else
! cancelMode = CONFLICT_MODE_FATAL;
break;
default:
*** a/src/backend/utils/time/tqual.c
--- b/src/backend/utils/time/tqual.c
***************
*** 1259,1265 **** XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
/*
* Data lives in different places depending upon when snapshot taken
*/
! if (snapshot->takenDuringRecovery)
{
/*
* If the snapshot contains full subxact data, the fastest way to check
--- 1259,1265 ----
/*
* Data lives in different places depending upon when snapshot taken
*/
! if (!snapshot->takenDuringRecovery)
{
/*
* If the snapshot contains full subxact data, the fastest way to check
*** a/src/include/access/nbtree.h
--- b/src/include/access/nbtree.h
***************
*** 536,545 **** typedef BTScanOpaqueData *BTScanOpaque;
#define SK_BT_DESC (INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
#define SK_BT_NULLS_FIRST (INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
- /* XXX probably needs new RMgr call to do this cleanly */
- extern bool btree_is_cleanup_record(uint8 info);
- extern bool btree_needs_cleanup_lock(uint8 info);
-
/*
* prototypes for functions in nbtree.c (external entry points for btree)
*/
--- 536,541 ----
*** a/src/include/storage/procarray.h
--- b/src/include/storage/procarray.h
***************
*** 54,60 **** extern int GetTransactionsInCommit(TransactionId **xids_p);
extern bool HaveTransactionsInCommit(TransactionId *xids, int nxids);
extern PGPROC *BackendPidGetProc(int pid);
- extern PGPROC *BackendXidGetProc(TransactionId xid);
extern int BackendXidGetPid(TransactionId xid);
extern bool IsBackendPid(int pid);
--- 54,59 ----
On Sat, Sep 26, 2009 at 5:49 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
Just a note to say that Hot Standby patch is now on git repository
git://git.postgresql.org/git/users/simon/postgres
Branch name: hot_standby
Awesome! Thanks for taking the time to get this set up.
The complete contents of that repository are BSD licenced contributions
to the PostgreSQL project.
Excellent...
Any further changes to that will be by agreement here on hackers. From
now, I will be submitting each individual change as patch-on-patch to
allow people to see and discuss them and to confirm them as open source
contributions.
Sounds good.
I request anybody else interested to do the same to allow
us to work together. All contributions welcome.My record of agreed changes is here
http://wiki.postgresql.org/wiki/Hot_Standby#Remaining_Work_ItemsYou'll notice that I've already completed 8 changes (10 commits); those
are all fairly minor changes, so submitted here as a combined patch.
There are 9 pending changes, so far, none of which appear to be major
obstacles to resolve. Many thanks to Heikki for a thorough review which
has identified nearly all of those change requests.I estimate that making the remaining changes noted on the Wiki and fully
testing them will take at least 2 weeks. Gabriele Bartolini is assisting
in this area, though neither of us are able to work full time on this.
We still have ample time to complete the project in this release.
Sounds like you are making rapid progress. If you think there's
something useful I could do, let me know and I'll take a look.
...Robert
On Sat, 2009-09-26 at 09:29 -0400, Robert Haas wrote:
I estimate that making the remaining changes noted on the Wiki and
fully
testing them will take at least 2 weeks. Gabriele Bartolini is assisting
in this area, though neither of us are able to work full time on this.
We still have ample time to complete the project in this release.Sounds like you are making rapid progress.
Only because Heikki has put so much effort into review and because most
of the remaining issues are coding related, rather than theoretical.
We moving along and are in no way threatened by time.
If you think there's
something useful I could do, let me know and I'll take a look.
I feel like I need a better way of unit testing new code. Some of the
code in the patch is to handle corner cases, so recreating them is
fairly hard. It is a nagging feeling that I am missing some knowledge
here and would welcome some insight, or research, into better ways of
doing general case unit testing.
--
Simon Riggs www.2ndQuadrant.com
On 09/26/2009 10:04 AM, Simon Riggs wrote:
If you think there's
something useful I could do, let me know and I'll take a look.I feel like I need a better way of unit testing new code. Some of the
code in the patch is to handle corner cases, so recreating them is
fairly hard. It is a nagging feeling that I am missing some knowledge
here and would welcome some insight, or research, into better ways of
doing general case unit testing.
You might try and steal ideas from EasyMock / PowerMock - but not sure
how well the ideas map to C.
Generally it means allowing the functions to be called from a "mock"
environment, where subroutine calls that might be called are stubbed out
to return sample data that would simulate your scenario. Object oriented
languages that require every object to provide an interface where most
object methods can be overridden are more ideal for performing this sort
of test.
I rarely ever see this sort of stuff in FOSS projects, and never that I
can remember in FOSS C projects. It's not easy, though.
I assume you are doing it through code changing right now. Commenting
out lines, replacing them with others, etc?
Cheers,
mark
--
Mark Mielke<mark@mielke.cc>
On Sat, Sep 26, 2009 at 10:45:17AM -0400, Mark Mielke wrote:
On 09/26/2009 10:04 AM, Simon Riggs wrote:
If you think there's
something useful I could do, let me know and I'll take a look.I feel like I need a better way of unit testing new code. Some of the
code in the patch is to handle corner cases, so recreating them is
fairly hard. It is a nagging feeling that I am missing some knowledge
here and would welcome some insight, or research, into better ways of
doing general case unit testing.You might try and steal ideas from EasyMock / PowerMock - but not
sure how well the ideas map to C.Generally it means allowing the functions to be called from a "mock"
environment, where subroutine calls that might be called are stubbed
out to return sample data that would simulate your scenario. Object
oriented languages that require every object to provide an interface
where most object methods can be overridden are more ideal for
performing this sort of test.I rarely ever see this sort of stuff in FOSS projects, and never
that I can remember in FOSS C projects. It's not easy, though.I assume you are doing it through code changing right now.
Commenting out lines, replacing them with others, etc?Cheers,
mark--
Mark Mielke<mark@mielke.cc>
There are a variety of projects dedicated to creating C unit test
frameworks. I don't have a lot of experience with them, but I have heard
good things about check and cunit. Here's a link I found with a longer
list of frameworks. http://www.opensourcetesting.org/unit_c.php
--
--Dan
I feel like I need a better way of unit testing new code. Some of the
code in the patch is to handle corner cases, so recreating them is
fairly hard. It is a nagging feeling that I am missing some knowledge
here and would welcome some insight, or research, into better ways of
doing general case unit testing.
There's always pgtap. Whenever we find a new corner case, we add it to
the development test suite.
--
Josh Berkus
PostgreSQL Experts Inc.
www.pgexperts.com
On 09/26/2009 02:28 PM, Dan Colish wrote:
There are a variety of projects dedicated to creating C unit test
frameworks. I don't have a lot of experience with them, but I have heard
good things about check and cunit. Here's a link I found with a longer
list of frameworks. http://www.opensourcetesting.org/unit_c.php
Looking at check and cunit - I don't see what sort of mock function
facility they would provide? One part of unit testing is arranging for
functions to be called, tested, and results reported on. This can take
you a certain amount of the way. "Pure" functions, for example, that
always generate the same output for the same input parameters, are
perfect for this situation. Perhaps test how a qsort() or bsearch()
method works under various scenarios?
Most real life code gets a little more complicated. For example, what if
we want to simulate a network failure or "out of disk space" condition?
What if we want to test out what happens when the Y2038 date is reached?
This requires either complex test case setup that is difficult to run
reproducibly, or another approach - "mock". It means doing things like
overriding the write() method, and making it return successful N times,
and then failing on the (N+1)th time with ENOSPC. It means overriding
the gettimeofday() method to return a time in the future. A major
benefit of this sort of testing is that it should not require source
changes in order to perform the test. This sort of stuff is a LOT easier
to do in OO languages. I see it done in Java a lot. I can't remember
ever having seen it done in C. I think it's just too hard compared to
the value obtained from the effort.
In your list above, it does show a few attempts - CMock sticks out as a
for example. It looks more complicated, though. It takes a .h file and
generates stubs for you to fill in? That could be difficult to manage
for a large project with thousands or many times more unit tests. OO is
easier because you can override *only* particular methods, and you can
safely call the super method that it overrides to provide the underlying
behaviour in the success cases.
Cheers,
mark
--
Mark Mielke<mark@mielke.cc>
On Sep 26, 2009, at 12:33 PM, Josh Berkus wrote:
There's always pgtap. Whenever we find a new corner case, we add it
to
the development test suite.
Also, for C TAP, there's [libtap](http://jc.ngo.org.uk/trac-bin/trac.cgi/wiki/LibTap
). You can then use `prove` which you likely already have on your
system, to harness the test output.
Best,
David
Mark Mielke escribi�:
Most real life code gets a little more complicated. For example,
what if we want to simulate a network failure or "out of disk space"
condition? What if we want to test out what happens when the Y2038
date is reached? This requires either complex test case setup that
is difficult to run reproducibly, or another approach - "mock". It
means doing things like overriding the write() method, and making it
return successful N times, and then failing on the (N+1)th time with
ENOSPC.
I remember a kernel simulator based on User-Mode Linux called UMLsim,
with which you could stuff like this. It's dead at this point though,
and I don't know if there's any replacement.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Per Simon's request, for the benefit of the archive, here's all the
changes I've done on the patch since he posted the initial version for
review for this commitfest as incremental patches. This is extracted
from my git repository at
git://git.postgresql.org/git/users/heikki/postgres.git.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Attachments:
hs-riggs-branch-20090927.tar.gzapplication/x-gzip; name=hs-riggs-branch-20090927.tar.gzDownload
� ~D�J �[{s�8���O�����C�������8�k�����WS[*��$�)RC��uw���� E*������U�*1%h ����F���_�Yt/�:���/�_�"_:�3F1~��d����Ajg.o���}��u:tm������6���w�f���u��f��F�������7���>�ND��4Pa(���vO=���G38l��^�w��?n���7����f����^�����7��(�+9�f���Z(�*�y ����Spj8N���S��j������wT����"�/�F�5h��*R:�V�e������F���^�������_?��>A�y�����i,��k,����X0[����*��;p�w'C�@'Q,&�@���yy"�����������S�2�4Q�>��7E����}�9�C�^��2�
�L!�p��S=�t��M��\o 5xS�<q�*�2NT����t{��@���j�W�1����8x����������Mo�p�q8����d/@�x�h����g���OPo�{5d]z�7>��!=?
���cj���w>�|yq{Da�
�����7pBT�)
�N�Wy���
W-�4���/��p(/9C�N�W����W�+�����Rz��I"��=�������iC��Oh {����K���@L4|��Nk�2<���ltu=�>�
�~~�<k���Z����]�>��2
CN
C[;����]�0�}���N��0���9��q���+A�����$���G������TB�LeV������,��?�<
�4�����a6��R��k���|��'A%?hP�y'�=H"K�� dr�G$���j��o��8�Kj�"�gr��q��L�����PJ?�!�E�Kn���)��{4K���eH}��D����c��G�3�����3�� Jc���'b��I��Ht(�z%Nu�q��E�����!�������JjKj1E'�
d>��V�eH�wH�F+���vX�9|��������:MT�n��X�;3��D2!�Q(�9k���U�S8��6o�~&'��en
}��e�Z�i���)\�$�C
�_NOA�5�Z��<JH*
�iH���Z�
r����4�M��o���^dJK����q ���%CR!��}�
�e��%u��qzrP�g��Z�j�;%�C��*�_�/�O���X�4HP�P�>�c�J��KH���g+*~B����J�}9��c�a������
%z4�He��x�J�,g�?OoHe��o���N>+�Y����������]����c6r�`��3w�>DA�'��d;��"+h}�=�C?�����f^�u�f���C�������[n,����^X�%�����+����0���C���T��A�� ��{r�L�%�&{f7(p�r�^���7(��vT� ��O��h�ote_��3�{�n{;�D�k$���q)�^��%�B_��S.���{y�����d����T�w���I�-�J���4�����>�:�
+�e�3cf� 3� ��������w<��pu����:'��5�]
��M/�f��Xo����D�L��xw�T���a�p{{�(<���Q?0_Vj���&���`4�_��������d��������E����2���[�@\S������f=B�d4�H�
�4�Y�� ����G��|�c\A�!�.��Q58�x�����J:_��D�>��ZD��:��"0�j����El��2�-
�kM���rH&���7�9a�{ ���>Og.��hC��Z?� ��`�)6wiYj�i�L���t��H�OQr����~$�� ��F��c#���]#�E��t�KpI�AKtv4Yk��
�'�o����
tlj2����;2B3����a�K��(K�t��X�����-:^AA5kTtA8?���0��#+X)��a�����8���o�j,;m��2t�JI�.��N��0 F���v>@e�y��U`��y�q�#�j����8�
�T9�r�����i*�2�,������5w0h������]��z��d4���Q�C%�eh��\��M|����M��������������g��(
����t��5�D*�,K(���2z��m�����:����xm'z5��T e���s�N�Z���z���}�F�~dS��%R��'����>��?��-��H������k��f���W������o��n6��.Bt��\W�;�F�1�:����^�y�����/R�A����6�/[�/[�m������`����Ie�y_���^OfG���� ��k����Z��D�p��l$L���`8u0Q!����s�i�����d�yG��>t�X�����������v��w@��&�?����Z7W�����M�9[M/[������v���
�tn����th_e��b���p3��M��0p���;��|{�X~���[YHt�<l�������J�W�D[;,��1��V���}{�[���lI��*q��\��-��0G��b�AtN9 Q���$�7��gl3�TM��M2c���#WSs,B��
!���������!���3�� *���`K(�� �F�]�O)�������p=|wz���Z#���XPRH�����o���M+)O��pu?��g�|���EJa����S�id�6��R'�r�F����8Y��L�F�������|���`F�X)��Am�Ji�O+�0D�*fs��l��q���e�i,��?S!z�X���%}��L`��|�
�k�����FV�1��P����c�$��(������/H���k�3c����� 4��@���i%}��6�/2��*������n�n��7�t� ���4R(T���������h� ��X=��TFbdM%��4��,���I�����Af��Rr.s:S�������Wm��N�h&E��%�!��bCNQ� 23YH��~+�b:�3�D+�dA��f��?�ado�p$(d�OG4t��\��s��e����� g�����Fa���(��E�������(!�G�2S�)}J���[����}�:�f��f{��:���T21fZ�y!�(/�"L�,���<]%�f� ��'��5Eyl�If�:|�f�B�B������8��QC�z���Y�� ���m��m,���w�{:b�E"��=>8+Q����e�mZ�K�0s:�;�sm��lZ�T ���j�zSK��3�e�A���"