> Can you test whether using the buffer access strategy is a win or a > loss? Most of that gain is probably coming from the reduction in > pinning.
Patch resnapped to HEAD, with straightforward adjustments to compensate for Heikki's changes to the ReadBuffer interface. See attached. New testing results, now with and without BAS: --TRUNK-- Time: 17945.523 ms Time: 18682.172 ms Time: 17047.841 ms Time: 16344.442 ms Time: 18727.417 ms --PATCHED-- Time: 13323.772 ms Time: 13869.724 ms Time: 14043.666 ms Time: 13934.132 ms Time: 13193.702 ms --PATCHED with BAS disabled-- Time: 14460.432 ms Time: 14745.206 ms Time: 14345.973 ms Time: 14601.448 ms Time: 16535.167 ms I'm not sure why the BAS seemed to be slowing things down before. Maybe it's different if we're copying into a pre-existing table, so that WAL is enabled? Or it could have just been a fluke - the numbers were close. I'll try to run some additional tests if time permits. ...Robert
Index: src/backend/access/heap/heapam.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/heapam.c,v retrieving revision 1.268 diff -c -r1.268 heapam.c *** src/backend/access/heap/heapam.c 31 Oct 2008 19:40:26 -0000 1.268 --- src/backend/access/heap/heapam.c 1 Nov 2008 17:17:16 -0000 *************** *** 1798,1803 **** --- 1798,1827 ---- } } + /* + * GetBulkInsertState - set up for a bulk insert + */ + BulkInsertState + GetBulkInsertState(void) + { + BulkInsertState bistate; + + bistate = palloc(sizeof(struct BulkInsertStateData)); + bistate->strategy = GetAccessStrategy(BAS_BULKWRITE); + bistate->last_pin = InvalidBuffer; + return bistate; + } + + /* + * FreeBulkInsertState - clean up after finishing a bulk insert + */ + void + FreeBulkInsertState(BulkInsertState bistate) + { + if (bistate->last_pin != InvalidBuffer) + ReleaseBuffer(bistate->last_pin); + FreeAccessStrategy(bistate->strategy); + } /* * heap_insert - insert tuple into a heap *************** *** 1805,1821 **** * The new tuple is stamped with current transaction ID and the specified * command ID. * ! * If use_wal is false, the new tuple is not logged in WAL, even for a ! * non-temp relation. Safe usage of this behavior requires that we arrange ! * that all new tuples go into new pages not containing any tuples from other ! * transactions, and that the relation gets fsync'd before commit. * (See also heap_sync() comments) * ! * use_fsm is passed directly to RelationGetBufferForTuple, which see for ! * more info. * ! * Note that use_wal and use_fsm will be applied when inserting into the ! * heap's TOAST table, too, if the tuple requires any out-of-line data. * * The return value is the OID assigned to the tuple (either here or by the * caller), or InvalidOid if no OID. The header fields of *tup are updated --- 1829,1846 ---- * The new tuple is stamped with current transaction ID and the specified * command ID. * ! * If the HEAP_INSERT_SKIP_WAL option is supplied, the new tuple is not logged ! * in WAL, even for a non-temp relation. Safe usage of this behavior requires ! * that we arrange that all new tuples go into new pages not containing any ! * tuples from other transactions, and that the relation gets fsync'd before ! * commit. * (See also heap_sync() comments) * ! * The HEAP_INSERT_SKIP_FSM option is passed directly to ! * RelationGetBufferForTuple, which see for more info. * ! * Note that options will be applied when inserting into the heap's TOAST ! * table, too, if the tuple requires any out-of-line data. * * The return value is the OID assigned to the tuple (either here or by the * caller), or InvalidOid if no OID. The header fields of *tup are updated *************** *** 1825,1831 **** */ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, ! bool use_wal, bool use_fsm) { TransactionId xid = GetCurrentTransactionId(); HeapTuple heaptup; --- 1850,1856 ---- */ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, ! int options, BulkInsertState bistate) { TransactionId xid = GetCurrentTransactionId(); HeapTuple heaptup; *************** *** 1877,1890 **** heaptup = tup; } else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) ! heaptup = toast_insert_or_update(relation, tup, NULL, ! use_wal, use_fsm); else heaptup = tup; /* Find buffer to insert this tuple into */ buffer = RelationGetBufferForTuple(relation, heaptup->t_len, ! InvalidBuffer, use_fsm); /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); --- 1902,1914 ---- heaptup = tup; } else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) ! heaptup = toast_insert_or_update(relation, tup, NULL, options); else heaptup = tup; /* Find buffer to insert this tuple into */ buffer = RelationGetBufferForTuple(relation, heaptup->t_len, ! InvalidBuffer, options, bistate); /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); *************** *** 1905,1911 **** MarkBufferDirty(buffer); /* XLOG stuff */ ! if (use_wal && !relation->rd_istemp) { xl_heap_insert xlrec; xl_heap_header xlhdr; --- 1929,1935 ---- MarkBufferDirty(buffer); /* XLOG stuff */ ! if ((options & HEAP_INSERT_SKIP_WAL) == 0 && !relation->rd_istemp) { xl_heap_insert xlrec; xl_heap_header xlhdr; *************** *** 1963,1969 **** END_CRIT_SECTION(); ! UnlockReleaseBuffer(buffer); /* * If tuple is cachable, mark it for invalidation from the caches in case --- 1987,1996 ---- END_CRIT_SECTION(); ! /* Release the lock, but keep the buffer pinned if doing bulk insert. */ ! LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ! if (!bistate) ! ReleaseBuffer(buffer); /* * If tuple is cachable, mark it for invalidation from the caches in case *************** *** 2000,2006 **** Oid simple_heap_insert(Relation relation, HeapTuple tup) { ! return heap_insert(relation, tup, GetCurrentCommandId(true), true, true); } /* --- 2027,2033 ---- Oid simple_heap_insert(Relation relation, HeapTuple tup) { ! return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); } /* *************** *** 2595,2602 **** if (need_toast) { /* Note we always use WAL and FSM during updates */ ! heaptup = toast_insert_or_update(relation, newtup, &oldtup, ! true, true); newtupsize = MAXALIGN(heaptup->t_len); } else --- 2622,2628 ---- if (need_toast) { /* Note we always use WAL and FSM during updates */ ! heaptup = toast_insert_or_update(relation, newtup, &oldtup, 0); newtupsize = MAXALIGN(heaptup->t_len); } else *************** *** 2623,2629 **** { /* Assume there's no chance to put heaptup on same page. */ newbuf = RelationGetBufferForTuple(relation, heaptup->t_len, ! buffer, true); } else { --- 2649,2655 ---- { /* Assume there's no chance to put heaptup on same page. */ newbuf = RelationGetBufferForTuple(relation, heaptup->t_len, ! buffer, 0, NULL); } else { *************** *** 2640,2646 **** */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); newbuf = RelationGetBufferForTuple(relation, heaptup->t_len, ! buffer, true); } else { --- 2666,2672 ---- */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); newbuf = RelationGetBufferForTuple(relation, heaptup->t_len, ! buffer, 0, NULL); } else { Index: src/backend/access/heap/hio.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/hio.c,v retrieving revision 1.73 diff -c -r1.73 hio.c *** src/backend/access/heap/hio.c 30 Sep 2008 10:52:10 -0000 1.73 --- src/backend/access/heap/hio.c 1 Nov 2008 17:17:16 -0000 *************** *** 15,20 **** --- 15,21 ---- #include "postgres.h" + #include "access/heapam.h" #include "access/hio.h" #include "storage/bufmgr.h" #include "storage/freespace.h" *************** *** 80,92 **** * happen if space is freed in that page after heap_update finds there's not * enough there). In that case, the page will be pinned and locked only once. * ! * If use_fsm is true (the normal case), we use FSM to help us find free ! * space. If use_fsm is false, we always append a new empty page to the ! * end of the relation if the tuple won't fit on the current target page. * This can save some cycles when we know the relation is new and doesn't * contain useful amounts of free space. * ! * The use_fsm = false case is also useful for non-WAL-logged additions to a * relation, if the caller holds exclusive lock and is careful to invalidate * relation->rd_targblock before the first insertion --- that ensures that * all insertions will occur into newly added pages and not be intermixed --- 81,93 ---- * happen if space is freed in that page after heap_update finds there's not * enough there). In that case, the page will be pinned and locked only once. * ! * We normally use FSM to help us find free space. However, ! * if HEAP_INSERT_SKIP_FSM is specified, we just append a new empty page to ! * the end of the relation if the tuple won't fit on the current target page. * This can save some cycles when we know the relation is new and doesn't * contain useful amounts of free space. * ! * HEAP_INSERT_SKIP_FSM is also useful for non-WAL-logged additions to a * relation, if the caller holds exclusive lock and is careful to invalidate * relation->rd_targblock before the first insertion --- that ensures that * all insertions will occur into newly added pages and not be intermixed *************** *** 104,110 **** */ Buffer RelationGetBufferForTuple(Relation relation, Size len, ! Buffer otherBuffer, bool use_fsm) { Buffer buffer = InvalidBuffer; Page page; --- 105,112 ---- */ Buffer RelationGetBufferForTuple(Relation relation, Size len, ! Buffer otherBuffer, int options, ! BulkInsertState bistate) { Buffer buffer = InvalidBuffer; Page page; *************** *** 113,121 **** --- 115,127 ---- BlockNumber targetBlock, otherBlock; bool needLock; + bool use_fsm = (options & HEAP_INSERT_SKIP_FSM) == 0; len = MAXALIGN(len); /* be conservative */ + /* Bulk insert is not supported for updates, only inserts. */ + Assert(!bistate || otherBuffer == InvalidBuffer); + /* * If we're gonna fail for oversize tuple, do it right away */ *************** *** 186,192 **** * block if one was given, taking suitable care with lock ordering and * the possibility they are the same block. */ ! if (otherBuffer == InvalidBuffer) { /* easy case */ buffer = ReadBuffer(relation, targetBlock); --- 192,212 ---- * block if one was given, taking suitable care with lock ordering and * the possibility they are the same block. */ ! if (bistate) ! { ! if (bistate->last_pin == InvalidBuffer) ! bistate->last_pin = ReadBufferExtended(relation, ! MAIN_FORKNUM, targetBlock, RBM_NORMAL, bistate->strategy); ! else if (BufferGetBlockNumber(bistate->last_pin) != targetBlock) ! { ! ReleaseBuffer(bistate->last_pin); ! bistate->last_pin = ReadBufferExtended(relation, ! MAIN_FORKNUM, targetBlock, RBM_NORMAL, bistate->strategy); ! } ! buffer = bistate->last_pin; ! LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); ! } ! else if (otherBuffer == InvalidBuffer) { /* easy case */ buffer = ReadBuffer(relation, targetBlock); *************** *** 240,245 **** --- 260,267 ---- LockBuffer(otherBuffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); } + if (bistate) + bistate->last_pin = InvalidBuffer; /* Without FSM, always fall out of the loop and extend */ if (!use_fsm) *************** *** 274,280 **** * it worth keeping an accurate file length in shared memory someplace, * rather than relying on the kernel to do it for us? */ ! buffer = ReadBuffer(relation, P_NEW); /* * We can be certain that locking the otherBuffer first is OK, since it --- 296,311 ---- * it worth keeping an accurate file length in shared memory someplace, * rather than relying on the kernel to do it for us? */ ! if (bistate) ! { ! if (bistate->last_pin != InvalidBuffer) ! ReleaseBuffer(bistate->last_pin); ! buffer = ReadBufferExtended(relation, MAIN_FORKNUM, P_NEW, ! RBM_NORMAL, bistate->strategy); ! bistate->last_pin = buffer; ! } ! else ! buffer = ReadBuffer(relation, P_NEW); /* * We can be certain that locking the otherBuffer first is OK, since it Index: src/backend/access/heap/rewriteheap.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/rewriteheap.c,v retrieving revision 1.15 diff -c -r1.15 rewriteheap.c *** src/backend/access/heap/rewriteheap.c 11 Aug 2008 11:05:10 -0000 1.15 --- src/backend/access/heap/rewriteheap.c 1 Nov 2008 17:17:17 -0000 *************** *** 575,581 **** } else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) heaptup = toast_insert_or_update(state->rs_new_rel, tup, NULL, ! state->rs_use_wal, false); else heaptup = tup; --- 575,583 ---- } else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) heaptup = toast_insert_or_update(state->rs_new_rel, tup, NULL, ! HEAP_INSERT_SKIP_FSM | ! (state->rs_use_wal ? ! 0 : HEAP_INSERT_SKIP_WAL)); else heaptup = tup; Index: src/backend/access/heap/tuptoaster.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v retrieving revision 1.89 diff -c -r1.89 tuptoaster.c *** src/backend/access/heap/tuptoaster.c 19 Jun 2008 00:46:03 -0000 1.89 --- src/backend/access/heap/tuptoaster.c 1 Nov 2008 17:17:18 -0000 *************** *** 74,81 **** static void toast_delete_datum(Relation rel, Datum value); ! static Datum toast_save_datum(Relation rel, Datum value, ! bool use_wal, bool use_fsm); static struct varlena *toast_fetch_datum(struct varlena * attr); static struct varlena *toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length); --- 74,80 ---- static void toast_delete_datum(Relation rel, Datum value); ! static Datum toast_save_datum(Relation rel, Datum value, int options); static struct varlena *toast_fetch_datum(struct varlena * attr); static struct varlena *toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length); *************** *** 400,406 **** * Inputs: * newtup: the candidate new tuple to be inserted * oldtup: the old row version for UPDATE, or NULL for INSERT ! * use_wal, use_fsm: flags to be passed to heap_insert() for toast rows * Result: * either newtup if no toasting is needed, or a palloc'd modified tuple * that is what should actually get stored --- 399,405 ---- * Inputs: * newtup: the candidate new tuple to be inserted * oldtup: the old row version for UPDATE, or NULL for INSERT ! * options: options to be passed to heap_insert() for toast rows * Result: * either newtup if no toasting is needed, or a palloc'd modified tuple * that is what should actually get stored *************** *** 411,417 **** */ HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, ! bool use_wal, bool use_fsm) { HeapTuple result_tuple; TupleDesc tupleDesc; --- 410,416 ---- */ HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, ! int options) { HeapTuple result_tuple; TupleDesc tupleDesc; *************** *** 677,684 **** { old_value = toast_values[i]; toast_action[i] = 'p'; ! toast_values[i] = toast_save_datum(rel, toast_values[i], ! use_wal, use_fsm); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; --- 676,682 ---- { old_value = toast_values[i]; toast_action[i] = 'p'; ! toast_values[i] = toast_save_datum(rel, toast_values[i], options); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; *************** *** 728,735 **** i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; ! toast_values[i] = toast_save_datum(rel, toast_values[i], ! use_wal, use_fsm); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; --- 726,732 ---- i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; ! toast_values[i] = toast_save_datum(rel, toast_values[i], options); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; *************** *** 838,845 **** i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; ! toast_values[i] = toast_save_datum(rel, toast_values[i], ! use_wal, use_fsm); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; --- 835,841 ---- i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; ! toast_values[i] = toast_save_datum(rel, toast_values[i], options); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; *************** *** 1120,1127 **** * ---------- */ static Datum ! toast_save_datum(Relation rel, Datum value, ! bool use_wal, bool use_fsm) { Relation toastrel; Relation toastidx; --- 1116,1122 ---- * ---------- */ static Datum ! toast_save_datum(Relation rel, Datum value, int options) { Relation toastrel; Relation toastidx; *************** *** 1218,1224 **** memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); ! heap_insert(toastrel, toasttup, mycid, use_wal, use_fsm); /* * Create the index entry. We cheat a little here by not using --- 1213,1219 ---- memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); ! heap_insert(toastrel, toasttup, mycid, options, NULL); /* * Create the index entry. We cheat a little here by not using Index: src/backend/commands/copy.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/copy.c,v retrieving revision 1.299 diff -c -r1.299 copy.c *** src/backend/commands/copy.c 12 May 2008 20:01:59 -0000 1.299 --- src/backend/commands/copy.c 1 Nov 2008 17:17:20 -0000 *************** *** 1653,1660 **** MemoryContext oldcontext = CurrentMemoryContext; ErrorContextCallback errcontext; CommandId mycid = GetCurrentCommandId(true); ! bool use_wal = true; /* by default, use WAL logging */ ! bool use_fsm = true; /* by default, use FSM for free space */ Assert(cstate->rel); --- 1653,1660 ---- MemoryContext oldcontext = CurrentMemoryContext; ErrorContextCallback errcontext; CommandId mycid = GetCurrentCommandId(true); ! int options = 0; ! BulkInsertState bistate; Assert(cstate->rel); *************** *** 1707,1715 **** if (cstate->rel->rd_createSubid != InvalidSubTransactionId || cstate->rel->rd_newRelfilenodeSubid != InvalidSubTransactionId) { ! use_fsm = false; if (!XLogArchivingActive()) ! use_wal = false; } if (pipe) --- 1707,1715 ---- if (cstate->rel->rd_createSubid != InvalidSubTransactionId || cstate->rel->rd_newRelfilenodeSubid != InvalidSubTransactionId) { ! options |= HEAP_INSERT_SKIP_FSM; if (!XLogArchivingActive()) ! options |= HEAP_INSERT_SKIP_WAL; } if (pipe) *************** *** 1886,1891 **** --- 1886,1893 ---- cstate->cur_attname = NULL; cstate->cur_attval = NULL; + bistate = GetBulkInsertState(); + /* Set up callback to identify error line number */ errcontext.callback = copy_in_error_callback; errcontext.arg = (void *) cstate; *************** *** 2111,2117 **** ExecConstraints(resultRelInfo, slot, estate); /* OK, store the tuple and create index entries for it */ ! heap_insert(cstate->rel, tuple, mycid, use_wal, use_fsm); if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); --- 2113,2119 ---- ExecConstraints(resultRelInfo, slot, estate); /* OK, store the tuple and create index entries for it */ ! heap_insert(cstate->rel, tuple, mycid, options, bistate); if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); *************** *** 2129,2134 **** --- 2131,2137 ---- } /* Done, clean up */ + FreeBulkInsertState(bistate); error_context_stack = errcontext.previous; MemoryContextSwitchTo(oldcontext); *************** *** 2167,2173 **** * If we skipped writing WAL, then we need to sync the heap (but not * indexes since those use WAL anyway) */ ! if (!use_wal) heap_sync(cstate->rel); } --- 2170,2176 ---- * If we skipped writing WAL, then we need to sync the heap (but not * indexes since those use WAL anyway) */ ! if ((options & HEAP_INSERT_SKIP_WAL) != 0) heap_sync(cstate->rel); } Index: src/backend/executor/execMain.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/executor/execMain.c,v retrieving revision 1.314 diff -c -r1.314 execMain.c *** src/backend/executor/execMain.c 31 Oct 2008 21:07:54 -0000 1.314 --- src/backend/executor/execMain.c 1 Nov 2008 17:17:22 -0000 *************** *** 1623,1630 **** * t_self field. */ newId = heap_insert(resultRelationDesc, tuple, ! estate->es_output_cid, ! true, true); IncrAppended(); (estate->es_processed)++; --- 1623,1629 ---- * t_self field. */ newId = heap_insert(resultRelationDesc, tuple, ! estate->es_output_cid, 0, NULL); IncrAppended(); (estate->es_processed)++; *************** *** 2621,2627 **** DestReceiver pub; /* publicly-known function pointers */ EState *estate; /* EState we are working with */ Relation rel; /* Relation to write to */ ! bool use_wal; /* do we need to WAL-log our writes? */ } DR_intorel; /* --- 2620,2627 ---- DestReceiver pub; /* publicly-known function pointers */ EState *estate; /* EState we are working with */ Relation rel; /* Relation to write to */ ! int hi_options; /* heap_insert performance options */ ! BulkInsertState bistate; /* bulk insert state */ } DR_intorel; /* *************** *** 2753,2763 **** myState = (DR_intorel *) queryDesc->dest; Assert(myState->pub.mydest == DestIntoRel); myState->estate = estate; /* ! * We can skip WAL-logging the insertions, unless PITR is in use. */ ! myState->use_wal = XLogArchivingActive(); myState->rel = intoRelationDesc; /* use_wal off requires rd_targblock be initially invalid */ --- 2753,2766 ---- myState = (DR_intorel *) queryDesc->dest; Assert(myState->pub.mydest == DestIntoRel); myState->estate = estate; + myState->bistate = GetBulkInsertState(); /* ! * We can skip WAL-logging the insertions, unless PITR is in use. We ! * can skip the FSM in any case. */ ! myState->hi_options = HEAP_INSERT_SKIP_FSM | ! (XLogArchivingActive() ? 0 : HEAP_INSERT_SKIP_WAL); myState->rel = intoRelationDesc; /* use_wal off requires rd_targblock be initially invalid */ *************** *** 2775,2782 **** /* OpenIntoRel might never have gotten called */ if (myState && myState->pub.mydest == DestIntoRel && myState->rel) { /* If we skipped using WAL, must heap_sync before commit */ ! if (!myState->use_wal) heap_sync(myState->rel); /* close rel, but keep lock until commit */ --- 2778,2787 ---- /* OpenIntoRel might never have gotten called */ if (myState && myState->pub.mydest == DestIntoRel && myState->rel) { + FreeBulkInsertState(myState->bistate); + /* If we skipped using WAL, must heap_sync before commit */ ! if ((myState->hi_options & HEAP_INSERT_SKIP_WAL) != 0) heap_sync(myState->rel); /* close rel, but keep lock until commit */ *************** *** 2834,2841 **** heap_insert(myState->rel, tuple, myState->estate->es_output_cid, ! myState->use_wal, ! false); /* never any point in using FSM */ /* We know this is a newly created relation, so there are no indexes */ --- 2839,2846 ---- heap_insert(myState->rel, tuple, myState->estate->es_output_cid, ! myState->hi_options, ! myState->bistate); /* We know this is a newly created relation, so there are no indexes */ Index: src/backend/storage/buffer/README =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/storage/buffer/README,v retrieving revision 1.14 diff -c -r1.14 README *** src/backend/storage/buffer/README 21 Mar 2008 13:23:28 -0000 1.14 --- src/backend/storage/buffer/README 1 Nov 2008 17:17:23 -0000 *************** *** 235,240 **** --- 235,242 ---- buffer, resulting in excessive WAL flushing. Allowing VACUUM to update 256KB between WAL flushes should be more efficient. + Beginning in 8.4, COPY IN and CREATE TABLE AS SELECT also uses a ring buffer, + in order to avoid trashing the entire buffer arena. Background Writer's Processing ------------------------------ Index: src/backend/storage/buffer/freelist.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/storage/buffer/freelist.c,v retrieving revision 1.64 diff -c -r1.64 freelist.c *** src/backend/storage/buffer/freelist.c 1 Jan 2008 19:45:51 -0000 1.64 --- src/backend/storage/buffer/freelist.c 1 Nov 2008 17:17:23 -0000 *************** *** 387,392 **** --- 387,395 ---- case BAS_VACUUM: ring_size = 256 * 1024 / BLCKSZ; break; + case BAS_BULKWRITE: + ring_size = 256 * 1024 / BLCKSZ; + break; default: elog(ERROR, "unrecognized buffer access strategy: %d", Index: src/include/access/heapam.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/access/heapam.h,v retrieving revision 1.139 diff -c -r1.139 heapam.h *** src/include/access/heapam.h 8 Oct 2008 01:14:44 -0000 1.139 --- src/include/access/heapam.h 1 Nov 2008 17:17:24 -0000 *************** *** 31,36 **** --- 31,37 ---- LockTupleExclusive } LockTupleMode; + typedef struct BulkInsertStateData *BulkInsertState; /* ---------------- * function prototypes for heap access method *************** *** 86,93 **** ItemPointer tid); extern void setLastTid(const ItemPointer tid); extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, ! bool use_wal, bool use_fsm); extern HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, TransactionId *update_xmax, CommandId cid, Snapshot crosscheck, bool wait); --- 87,99 ---- ItemPointer tid); extern void setLastTid(const ItemPointer tid); + #define HEAP_INSERT_SKIP_WAL 0x0001 + #define HEAP_INSERT_SKIP_FSM 0x0002 + extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, ! int options, BulkInsertState bistate); ! BulkInsertState GetBulkInsertState(void); ! void FreeBulkInsertState(BulkInsertState); extern HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, TransactionId *update_xmax, CommandId cid, Snapshot crosscheck, bool wait); Index: src/include/access/hio.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/access/hio.h,v retrieving revision 1.36 diff -c -r1.36 hio.h *** src/include/access/hio.h 19 Jun 2008 00:46:06 -0000 1.36 --- src/include/access/hio.h 1 Nov 2008 17:17:24 -0000 *************** *** 18,26 **** #include "utils/relcache.h" #include "storage/buf.h" extern void RelationPutHeapTuple(Relation relation, Buffer buffer, HeapTuple tuple); extern Buffer RelationGetBufferForTuple(Relation relation, Size len, ! Buffer otherBuffer, bool use_fsm); #endif /* HIO_H */ --- 18,33 ---- #include "utils/relcache.h" #include "storage/buf.h" + /* private to access/heap/heapam.c and access/heap/hio.c */ + struct BulkInsertStateData { + BufferAccessStrategy strategy; + Buffer last_pin; + }; + extern void RelationPutHeapTuple(Relation relation, Buffer buffer, HeapTuple tuple); extern Buffer RelationGetBufferForTuple(Relation relation, Size len, ! Buffer otherBuffer, int options, ! BulkInsertState bistate); #endif /* HIO_H */ Index: src/include/access/tuptoaster.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/access/tuptoaster.h,v retrieving revision 1.41 diff -c -r1.41 tuptoaster.h *** src/include/access/tuptoaster.h 13 Jul 2008 20:45:47 -0000 1.41 --- src/include/access/tuptoaster.h 1 Nov 2008 17:17:24 -0000 *************** *** 93,99 **** */ extern HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, ! bool use_wal, bool use_fsm); /* ---------- * toast_delete - --- 93,99 ---- */ extern HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, ! int options); /* ---------- * toast_delete - Index: src/include/storage/bufmgr.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/storage/bufmgr.h,v retrieving revision 1.116 diff -c -r1.116 bufmgr.h *** src/include/storage/bufmgr.h 31 Oct 2008 15:05:00 -0000 1.116 --- src/include/storage/bufmgr.h 1 Nov 2008 17:17:25 -0000 *************** *** 28,34 **** BAS_NORMAL, /* Normal random access */ BAS_BULKREAD, /* Large read-only scan (hint bit updates are * ok) */ ! BAS_VACUUM /* VACUUM */ } BufferAccessStrategyType; /* Possible modes for ReadBufferExtended() */ --- 28,35 ---- BAS_NORMAL, /* Normal random access */ BAS_BULKREAD, /* Large read-only scan (hint bit updates are * ok) */ ! BAS_VACUUM, /* VACUUM */ ! BAS_BULKWRITE /* Large multi-block write (e.g. COPY IN) */ } BufferAccessStrategyType; /* Possible modes for ReadBufferExtended() */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers