Revert "Avoid creation of the free space map for small heap relations."
authorAmit Kapila <akapila@postgresql.org>
Mon, 28 Jan 2019 06:01:44 +0000 (11:31 +0530)
committerAmit Kapila <akapila@postgresql.org>
Mon, 28 Jan 2019 06:01:44 +0000 (11:31 +0530)
This reverts commit ac88d2962a96a9c7e83d5acfc28fe49a72812086.

16 files changed:
contrib/pageinspect/expected/page.out
contrib/pageinspect/sql/page.sql
doc/src/sgml/storage.sgml
src/backend/access/brin/brin.c
src/backend/access/brin/brin_pageops.c
src/backend/access/heap/hio.c
src/backend/access/heap/vacuumlazy.c
src/backend/access/transam/xact.c
src/backend/storage/freespace/README
src/backend/storage/freespace/freespace.c
src/backend/storage/freespace/indexfsm.c
src/include/storage/freespace.h
src/test/regress/expected/fsm.out [deleted file]
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/fsm.sql [deleted file]

index 11d56f49a4ba610344074751ddd4dd71c72bc869..3fcd9fbe6d9edba28c3a70c2d54feeaaaae90513 100644 (file)
@@ -1,69 +1,48 @@
 CREATE EXTENSION pageinspect;
-CREATE TABLE test_rel_forks (a int);
--- Make sure there are enough blocks in the heap for the FSM to be created.
-INSERT INTO test_rel_forks SELECT i from generate_series(1,1000) i;
--- set up FSM and VM
-VACUUM test_rel_forks;
+CREATE TABLE test1 (a int, b int);
+INSERT INTO test1 VALUES (16777217, 131584);
+VACUUM test1;  -- set up FSM
 -- The page contents can vary, so just test that it can be read
 -- successfully, but don't keep the output.
-SELECT octet_length(get_raw_page('test_rel_forks', 'main', 0)) AS main_0;
+SELECT octet_length(get_raw_page('test1', 'main', 0)) AS main_0;
  main_0 
 --------
    8192
 (1 row)
 
-SELECT octet_length(get_raw_page('test_rel_forks', 'main', 100)) AS main_100;
-ERROR:  block number 100 is out of range for relation "test_rel_forks"
-SELECT octet_length(get_raw_page('test_rel_forks', 'fsm', 0)) AS fsm_0;
+SELECT octet_length(get_raw_page('test1', 'main', 1)) AS main_1;
+ERROR:  block number 1 is out of range for relation "test1"
+SELECT octet_length(get_raw_page('test1', 'fsm', 0)) AS fsm_0;
  fsm_0 
 -------
   8192
 (1 row)
 
-SELECT octet_length(get_raw_page('test_rel_forks', 'fsm', 10)) AS fsm_10;
-ERROR:  block number 10 is out of range for relation "test_rel_forks"
-SELECT octet_length(get_raw_page('test_rel_forks', 'vm', 0)) AS vm_0;
+SELECT octet_length(get_raw_page('test1', 'fsm', 1)) AS fsm_1;
+ fsm_1 
+-------
+  8192
+(1 row)
+
+SELECT octet_length(get_raw_page('test1', 'vm', 0)) AS vm_0;
  vm_0 
 ------
  8192
 (1 row)
 
-SELECT octet_length(get_raw_page('test_rel_forks', 'vm', 1)) AS vm_1;
-ERROR:  block number 1 is out of range for relation "test_rel_forks"
+SELECT octet_length(get_raw_page('test1', 'vm', 1)) AS vm_1;
+ERROR:  block number 1 is out of range for relation "test1"
 SELECT octet_length(get_raw_page('xxx', 'main', 0));
 ERROR:  relation "xxx" does not exist
-SELECT octet_length(get_raw_page('test_rel_forks', 'xxx', 0));
+SELECT octet_length(get_raw_page('test1', 'xxx', 0));
 ERROR:  invalid fork name
 HINT:  Valid fork names are "main", "fsm", "vm", and "init".
-SELECT * FROM fsm_page_contents(get_raw_page('test_rel_forks', 'fsm', 0));
- fsm_page_contents 
--------------------
- 0: 147           +
- 1: 147           +
- 3: 147           +
- 7: 147           +
- 15: 147          +
- 31: 147          +
- 63: 147          +
- 127: 147         +
- 255: 147         +
- 511: 147         +
- 1023: 147        +
- 2047: 147        +
- 4095: 147        +
- fp_next_slot: 0  +
-(1 row)
-
-SELECT get_raw_page('test_rel_forks', 0) = get_raw_page('test_rel_forks', 'main', 0);
+SELECT get_raw_page('test1', 0) = get_raw_page('test1', 'main', 0);
  ?column? 
 ----------
  t
 (1 row)
 
-DROP TABLE test_rel_forks;
-CREATE TABLE test1 (a int, b int);
-INSERT INTO test1 VALUES (16777217, 131584);
 SELECT pagesize, version FROM page_header(get_raw_page('test1', 0));
  pagesize | version 
 ----------+---------
@@ -83,6 +62,26 @@ SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bi
  {"\\x01000001","\\x00020200"}
 (1 row)
 
+SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
+ fsm_page_contents 
+-------------------
+ 0: 254           +
+ 1: 254           +
+ 3: 254           +
+ 7: 254           +
+ 15: 254          +
+ 31: 254          +
+ 63: 254          +
+ 127: 254         +
+ 255: 254         +
+ 511: 254         +
+ 1023: 254        +
+ 2047: 254        +
+ 4095: 254        +
+ fp_next_slot: 0  +
+(1 row)
+
 DROP TABLE test1;
 -- check that using any of these functions with a partitioned table or index
 -- would fail
index 0ff1f1a389bbc4fe0ab9316a5828ab710e88cbff..8ac999183752e160fce3202e5524b9ed840c5934 100644 (file)
@@ -1,35 +1,26 @@
 CREATE EXTENSION pageinspect;
 
-CREATE TABLE test_rel_forks (a int);
--- Make sure there are enough blocks in the heap for the FSM to be created.
-INSERT INTO test_rel_forks SELECT i from generate_series(1,1000) i;
+CREATE TABLE test1 (a int, b int);
+INSERT INTO test1 VALUES (16777217, 131584);
 
--- set up FSM and VM
-VACUUM test_rel_forks;
+VACUUM test1;  -- set up FSM
 
 -- The page contents can vary, so just test that it can be read
 -- successfully, but don't keep the output.
 
-SELECT octet_length(get_raw_page('test_rel_forks', 'main', 0)) AS main_0;
-SELECT octet_length(get_raw_page('test_rel_forks', 'main', 100)) AS main_100;
+SELECT octet_length(get_raw_page('test1', 'main', 0)) AS main_0;
+SELECT octet_length(get_raw_page('test1', 'main', 1)) AS main_1;
 
-SELECT octet_length(get_raw_page('test_rel_forks', 'fsm', 0)) AS fsm_0;
-SELECT octet_length(get_raw_page('test_rel_forks', 'fsm', 10)) AS fsm_10;
+SELECT octet_length(get_raw_page('test1', 'fsm', 0)) AS fsm_0;
+SELECT octet_length(get_raw_page('test1', 'fsm', 1)) AS fsm_1;
 
-SELECT octet_length(get_raw_page('test_rel_forks', 'vm', 0)) AS vm_0;
-SELECT octet_length(get_raw_page('test_rel_forks', 'vm', 1)) AS vm_1;
+SELECT octet_length(get_raw_page('test1', 'vm', 0)) AS vm_0;
+SELECT octet_length(get_raw_page('test1', 'vm', 1)) AS vm_1;
 
 SELECT octet_length(get_raw_page('xxx', 'main', 0));
-SELECT octet_length(get_raw_page('test_rel_forks', 'xxx', 0));
-
-SELECT * FROM fsm_page_contents(get_raw_page('test_rel_forks', 'fsm', 0));
-
-SELECT get_raw_page('test_rel_forks', 0) = get_raw_page('test_rel_forks', 'main', 0);
+SELECT octet_length(get_raw_page('test1', 'xxx', 0));
 
-DROP TABLE test_rel_forks;
-
-CREATE TABLE test1 (a int, b int);
-INSERT INTO test1 VALUES (16777217, 131584);
+SELECT get_raw_page('test1', 0) = get_raw_page('test1', 'main', 0);
 
 SELECT pagesize, version FROM page_header(get_raw_page('test1', 0));
 
@@ -38,6 +29,8 @@ SELECT page_checksum(get_raw_page('test1', 0), 0) IS NOT NULL AS silly_checksum_
 SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bits)
     FROM heap_page_items(get_raw_page('test1', 0));
 
+SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
+
 DROP TABLE test1;
 
 -- check that using any of these functions with a partitioned table or index
index cbdad0c3fb5759fd19924a567b85a5e8f16d4d36..8ef2ac80106052423a2323b1d31822006b2162f2 100644 (file)
@@ -590,13 +590,12 @@ tuple would otherwise be too big.
 <indexterm><primary>FSM</primary><see>Free Space Map</see></indexterm>
 
 <para>
-Each heap relation, unless it is very small, and each index relation, except
-for hash indexes, has a Free Space Map (FSM) to keep track of available
-space in the relation. It's stored alongside the main relation data in a
-separate relation fork, named after the filenode number of the relation, plus
-a <literal>_fsm</literal> suffix. For example, if the filenode of a relation
-is 12345, the FSM is stored in a file called <filename>12345_fsm</filename>,
-in the same directory as the main relation file.
+Each heap and index relation, except for hash indexes, has a Free Space Map
+(FSM) to keep track of available space in the relation. It's stored
+alongside the main relation data in a separate relation fork, named after the
+filenode number of the relation, plus a <literal>_fsm</literal> suffix. For example,
+if the filenode of a relation is 12345, the FSM is stored in a file called
+<filename>12345_fsm</filename>, in the same directory as the main relation file.
 </para>
 
 <para>
index 8f008dd0080d9d451ec40794f2cb2bc5b1feacda..467d91e681820917e232f423187532b7a3694f9e 100644 (file)
@@ -1150,7 +1150,7 @@ terminate_brin_buildstate(BrinBuildState *state)
                freespace = PageGetFreeSpace(page);
                blk = BufferGetBlockNumber(state->bs_currentInsertBuf);
                ReleaseBuffer(state->bs_currentInsertBuf);
-               RecordPageWithFreeSpace(state->bs_irel, blk, freespace, InvalidBlockNumber);
+               RecordPageWithFreeSpace(state->bs_irel, blk, freespace);
                FreeSpaceMapVacuumRange(state->bs_irel, blk, blk + 1);
        }
 
index 2eb354f948f964e816de9f6f3128e57a30eba138..164a46815565e31e272c1aa951fd274f5951435c 100644 (file)
@@ -310,7 +310,7 @@ brin_doupdate(Relation idxrel, BlockNumber pagesPerRange,
 
                if (extended)
                {
-                       RecordPageWithFreeSpace(idxrel, newblk, freespace, InvalidBlockNumber);
+                       RecordPageWithFreeSpace(idxrel, newblk, freespace);
                        FreeSpaceMapVacuumRange(idxrel, newblk, newblk + 1);
                }
 
@@ -461,7 +461,7 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange,
 
        if (extended)
        {
-               RecordPageWithFreeSpace(idxrel, blk, freespace, InvalidBlockNumber);
+               RecordPageWithFreeSpace(idxrel, blk, freespace);
                FreeSpaceMapVacuumRange(idxrel, blk, blk + 1);
        }
 
@@ -654,7 +654,7 @@ brin_page_cleanup(Relation idxrel, Buffer buf)
 
        /* Measure free space and record it */
        RecordPageWithFreeSpace(idxrel, BufferGetBlockNumber(buf),
-                                                       br_page_get_freespace(page), InvalidBlockNumber);
+                                                       br_page_get_freespace(page));
 }
 
 /*
@@ -703,7 +703,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
        /* Choose initial target page, re-using existing target if known */
        newblk = RelationGetTargetBlock(irel);
        if (newblk == InvalidBlockNumber)
-               newblk = GetPageWithFreeSpace(irel, itemsz, true);
+               newblk = GetPageWithFreeSpace(irel, itemsz);
 
        /*
         * Loop until we find a page with sufficient free space.  By the time we
@@ -895,7 +895,7 @@ brin_initialize_empty_new_buffer(Relation idxrel, Buffer buffer)
         * pages whose FSM records were forgotten in a crash.
         */
        RecordPageWithFreeSpace(idxrel, BufferGetBlockNumber(buffer),
-                                                       br_page_get_freespace(page), InvalidBlockNumber);
+                                                       br_page_get_freespace(page));
 }
 
 
index 4c3e774eee252c368b1ebc95b7079138d06d2647..3da0b49ccc4c14116d3ad87013559896cedd47b2 100644 (file)
@@ -239,14 +239,8 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
                 * Immediately update the bottom level of the FSM.  This has a good
                 * chance of making this page visible to other concurrently inserting
                 * backends, and we want that to happen without delay.
-                *
-                * Since we know the table will end up with extraBlocks additional
-                * pages, we pass the final number to avoid possible unnecessary
-                * system calls and to make sure the FSM is created when we add the
-                * first new page.
                 */
-               RecordPageWithFreeSpace(relation, blockNum, freespace,
-                                                               firstBlock + extraBlocks);
+               RecordPageWithFreeSpace(relation, blockNum, freespace);
        }
        while (--extraBlocks > 0);
 
@@ -383,9 +377,20 @@ RelationGetBufferForTuple(Relation relation, Size len,
                 * We have no cached target page, so ask the FSM for an initial
                 * target.
                 */
-               targetBlock = GetPageWithFreeSpace(relation,
-                                                                                  len + saveFreeSpace,
-                                                                                  false);
+               targetBlock = GetPageWithFreeSpace(relation, len + saveFreeSpace);
+
+               /*
+                * If the FSM knows nothing of the rel, try the last page before we
+                * give up and extend.  This avoids one-tuple-per-page syndrome during
+                * bootstrapping or in a recently-started system.
+                */
+               if (targetBlock == InvalidBlockNumber)
+               {
+                       BlockNumber nblocks = RelationGetNumberOfBlocks(relation);
+
+                       if (nblocks > 0)
+                               targetBlock = nblocks - 1;
+               }
        }
 
 loop:
@@ -479,14 +484,6 @@ loop:
                {
                        /* use this page as future insert target, too */
                        RelationSetTargetBlock(relation, targetBlock);
-
-                       /*
-                        * In case we used an in-memory map of available blocks, reset it
-                        * for next use.
-                        */
-                       if (targetBlock < HEAP_FSM_CREATION_THRESHOLD)
-                               FSMClearLocalMap();
-
                        return buffer;
                }
 
@@ -546,12 +543,9 @@ loop:
 
                        /*
                         * Check if some other backend has extended a block for us while
-                        * we were waiting on the lock.  We only check the FSM -- if there
-                        * isn't one we don't recheck the number of blocks.
+                        * we were waiting on the lock.
                         */
-                       targetBlock = GetPageWithFreeSpace(relation,
-                                                                                          len + saveFreeSpace,
-                                                                                          true);
+                       targetBlock = GetPageWithFreeSpace(relation, len + saveFreeSpace);
 
                        /*
                         * If some other waiter has already extended the relation, we
@@ -631,12 +625,5 @@ loop:
         */
        RelationSetTargetBlock(relation, BufferGetBlockNumber(buffer));
 
-       /*
-        * In case we used an in-memory map of available blocks, reset it for next
-        * use.  We do this unconditionally since after relation extension we
-        * can't skip this based on the targetBlock.
-        */
-       FSMClearLocalMap();
-
        return buffer;
 }
index 9cfa65ca47f04ab0c9b031555fe1f4fb0cda820a..37aa484ec3a971f475de26ed843467a6390d2290 100644 (file)
@@ -153,7 +153,7 @@ static BufferAccessStrategy vac_strategy;
 static void lazy_scan_heap(Relation onerel, int options,
                           LVRelStats *vacrelstats, Relation *Irel, int nindexes,
                           bool aggressive);
-static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, BlockNumber nblocks);
+static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
 static bool lazy_check_needs_freeze(Buffer buf, bool *hastup);
 static void lazy_vacuum_index(Relation indrel,
                                  IndexBulkDeleteResult **stats,
@@ -758,7 +758,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                        pgstat_progress_update_multi_param(2, hvp_index, hvp_val);
 
                        /* Remove tuples from heap */
-                       lazy_vacuum_heap(onerel, vacrelstats, nblocks);
+                       lazy_vacuum_heap(onerel, vacrelstats);
 
                        /*
                         * Forget the now-vacuumed tuples, and press on, but be careful
@@ -896,7 +896,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                        MarkBufferDirty(buf);
                        UnlockReleaseBuffer(buf);
 
-                       RecordPageWithFreeSpace(onerel, blkno, freespace, nblocks);
+                       RecordPageWithFreeSpace(onerel, blkno, freespace);
                        continue;
                }
 
@@ -935,7 +935,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                        }
 
                        UnlockReleaseBuffer(buf);
-                       RecordPageWithFreeSpace(onerel, blkno, freespace, nblocks);
+                       RecordPageWithFreeSpace(onerel, blkno, freespace);
                        continue;
                }
 
@@ -1332,7 +1332,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                 * taken if there are no indexes.)
                 */
                if (vacrelstats->num_dead_tuples == prev_dead_count)
-                       RecordPageWithFreeSpace(onerel, blkno, freespace, nblocks);
+                       RecordPageWithFreeSpace(onerel, blkno, freespace);
        }
 
        /* report that everything is scanned and vacuumed */
@@ -1394,7 +1394,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                /* Remove tuples from heap */
                pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,
                                                                         PROGRESS_VACUUM_PHASE_VACUUM_HEAP);
-               lazy_vacuum_heap(onerel, vacrelstats, nblocks);
+               lazy_vacuum_heap(onerel, vacrelstats);
                vacrelstats->num_index_scans++;
        }
 
@@ -1465,10 +1465,9 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
  * Note: the reason for doing this as a second pass is we cannot remove
  * the tuples until we've removed their index entries, and we want to
  * process index entry removal in batches as large as possible.
- * Note: nblocks is passed as an optimization for RecordPageWithFreeSpace().
  */
 static void
-lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, BlockNumber nblocks)
+lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
 {
        int                     tupindex;
        int                     npages;
@@ -1505,7 +1504,7 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, BlockNumber nblocks)
                freespace = PageGetHeapFreeSpace(page);
 
                UnlockReleaseBuffer(buf);
-               RecordPageWithFreeSpace(onerel, tblk, freespace, nblocks);
+               RecordPageWithFreeSpace(onerel, tblk, freespace);
                npages++;
        }
 
index 92bda87804326dd16389f6c387d5529b063024ed..0181976964c54b80dd4de9e545e52e19ebe7e1f3 100644 (file)
@@ -48,7 +48,6 @@
 #include "replication/walsender.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
-#include "storage/freespace.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
 #include "storage/proc.h"
@@ -2494,12 +2493,6 @@ AbortTransaction(void)
        pgstat_report_wait_end();
        pgstat_progress_end_command();
 
-       /*
-        * In case we aborted during RelationGetBufferForTuple(), clear the local
-        * map of heap pages.
-        */
-       FSMClearLocalMap();
-
        /* Clean up buffer I/O and buffer context locks, too */
        AbortBufferIO();
        UnlockBuffers();
@@ -4721,13 +4714,6 @@ AbortSubTransaction(void)
 
        pgstat_report_wait_end();
        pgstat_progress_end_command();
-
-       /*
-        * In case we aborted during RelationGetBufferForTuple(), clear the local
-        * map of heap pages.
-        */
-       FSMClearLocalMap();
-
        AbortBufferIO();
        UnlockBuffers();
 
index 0d3cd29772ea39334c0b91f38526709a59ee50db..e7ff23b76f79463388bd94afbbb792cf51b1c0dc 100644 (file)
@@ -8,41 +8,7 @@ free space to hold a tuple to be stored; or to determine that no such page
 exists and the relation must be extended by one page.  As of PostgreSQL 8.4
 each relation has its own, extensible free space map stored in a separate
 "fork" of its relation.  This eliminates the disadvantages of the former
-fixed-size FSM.  There are two exceptions:
-
-1. Hash indexes never have a FSM.
-2. For very small tables, a 3-page relation fork would be relatively large
-and wasteful, so to save space we refrain from creating the FSM if the
-heap has HEAP_FSM_CREATION_THRESHOLD pages or fewer.
-
-To locate free space in the latter case, we simply try pages directly without
-knowing ahead of time how much free space they have.  To maintain good
-performance, we create a local in-memory map of pages to try, and only mark
-every other page as available.  For example, in a 3-page heap, the local map
-would look like:
-
-ANAN
-0123
-
-Pages 0 and 2 are marked "available", and page 1 as "not available".
-Page 3 is beyond the end of the relation, so is likewise marked "not
-available".  First we try page 2, and if that doesn't have sufficient free
-space we try page 0 before giving up and extending the relation.  There may
-be some wasted free space on block 1, but if the relation extends to 4 pages:
-
-NANA
-0123
-
-We not only have the new page 3 at our disposal, we can now check page 1
-for free space as well.
-
-Once the FSM is created for a heap we don't remove it even if somebody deletes
-all the rows from the corresponding relation.  We don't think it is a useful
-optimization as it is quite likely that relation will again grow to the same
-size.
-
-FSM data structure
-------------------
+fixed-size FSM.
 
 It is important to keep the map small so that it can be searched rapidly.
 Therefore, we don't attempt to record the exact free space on a page.
@@ -226,3 +192,5 @@ TODO
 ----
 
 - fastroot to avoid traversing upper nodes with just 1 child
+- use a different system for tables that fit into one FSM page, with a
+  mechanism to switch to the real thing as it grows.
index 5f46391b681d8278e570e00be8809da39651dd4d..eee82860575cd72c5482fcf7f458ca4e09ffb3e5 100644 (file)
 #define FSM_ROOT_LEVEL (FSM_TREE_DEPTH - 1)
 #define FSM_BOTTOM_LEVEL 0
 
-/* Status codes for the local map. */
-
-/* Either already tried, or beyond the end of the relation */
-#define FSM_LOCAL_NOT_AVAIL 0x00
-
-/* Available to try */
-#define FSM_LOCAL_AVAIL                0x01
-
 /*
  * The internal FSM routines work on a logical addressing scheme. Each
  * level of the tree can be thought of as a separately addressable file.
@@ -97,17 +89,6 @@ typedef struct
 /* Address of the root page. */
 static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};
 
-/* Local map of block numbers for small heaps with no FSM. */
-typedef struct
-{
-       BlockNumber nblocks;
-       uint8           map[HEAP_FSM_CREATION_THRESHOLD];
-} FSMLocalMap;
-
-static FSMLocalMap fsm_local_map = {0, {FSM_LOCAL_NOT_AVAIL}};
-
-#define FSM_LOCAL_MAP_EXISTS (fsm_local_map.nblocks > 0)
-
 /* functions to navigate the tree */
 static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot);
 static FSMAddress fsm_get_parent(FSMAddress child, uint16 *slot);
@@ -126,14 +107,10 @@ static Size fsm_space_cat_to_avail(uint8 cat);
 /* workhorse functions for various operations */
 static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot,
                                   uint8 newValue, uint8 minValue);
-static void fsm_local_set(Relation rel, BlockNumber cur_nblocks);
 static BlockNumber fsm_search(Relation rel, uint8 min_cat);
-static BlockNumber fsm_local_search(void);
 static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr,
                                BlockNumber start, BlockNumber end,
                                bool *eof);
-static bool fsm_allow_writes(Relation rel, BlockNumber heapblk,
-                               BlockNumber nblocks, BlockNumber *get_nblocks);
 
 
 /******** Public API ********/
@@ -150,46 +127,13 @@ static bool fsm_allow_writes(Relation rel, BlockNumber heapblk,
  * amount of free space available on that page and then try again (see
  * RecordAndGetPageWithFreeSpace).  If InvalidBlockNumber is returned,
  * extend the relation.
- *
- * For very small heap relations that don't have a FSM, we try every other
- * page before extending the relation.  To keep track of which pages have
- * been tried, initialize a local in-memory map of pages.
  */
 BlockNumber
-GetPageWithFreeSpace(Relation rel, Size spaceNeeded, bool check_fsm_only)
+GetPageWithFreeSpace(Relation rel, Size spaceNeeded)
 {
        uint8           min_cat = fsm_space_needed_to_cat(spaceNeeded);
-       BlockNumber target_block,
-                               nblocks;
-
-       /* First try the FSM, if it exists. */
-       target_block = fsm_search(rel, min_cat);
-
-       if (target_block == InvalidBlockNumber &&
-               (rel->rd_rel->relkind == RELKIND_RELATION ||
-                rel->rd_rel->relkind == RELKIND_TOASTVALUE) &&
-               !check_fsm_only)
-       {
-               nblocks = RelationGetNumberOfBlocks(rel);
-
-               if (nblocks > HEAP_FSM_CREATION_THRESHOLD)
-               {
-                       /*
-                        * If the FSM knows nothing of the rel, try the last page before
-                        * we give up and extend.  This avoids one-tuple-per-page syndrome
-                        * during bootstrapping or in a recently-started system.
-                        */
-                       target_block = nblocks - 1;
-               }
-               else if (nblocks > 0)
-               {
-                       /* Create or update local map and get first candidate block. */
-                       fsm_local_set(rel, nblocks);
-                       target_block = fsm_local_search();
-               }
-       }
 
-       return target_block;
+       return fsm_search(rel, min_cat);
 }
 
 /*
@@ -200,47 +144,16 @@ GetPageWithFreeSpace(Relation rel, Size spaceNeeded, bool check_fsm_only)
  * also some effort to return a page close to the old page; if there's a
  * page with enough free space on the same FSM page where the old one page
  * is located, it is preferred.
- *
- * For very small heap relations that don't have a FSM, we update the local
- * map to indicate we have tried a page, and return the next page to try.
  */
 BlockNumber
 RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage,
                                                          Size oldSpaceAvail, Size spaceNeeded)
 {
-       int                     old_cat;
-       int                     search_cat;
+       int                     old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
+       int                     search_cat = fsm_space_needed_to_cat(spaceNeeded);
        FSMAddress      addr;
        uint16          slot;
        int                     search_slot;
-       BlockNumber nblocks = InvalidBlockNumber;
-
-       /* First try the local map, if it exists. */
-       if (FSM_LOCAL_MAP_EXISTS)
-       {
-               Assert((rel->rd_rel->relkind == RELKIND_RELATION ||
-                               rel->rd_rel->relkind == RELKIND_TOASTVALUE) &&
-                          fsm_local_map.map[oldPage] == FSM_LOCAL_AVAIL);
-
-               fsm_local_map.map[oldPage] = FSM_LOCAL_NOT_AVAIL;
-               return fsm_local_search();
-       }
-
-       if (!fsm_allow_writes(rel, oldPage, InvalidBlockNumber, &nblocks))
-       {
-               /*
-                * If we have neither a local map nor a FSM, we probably just
-                * tried the target block in the smgr relation entry and failed,
-                * so we'll need to create the local map.
-                */
-               fsm_local_set(rel, nblocks);
-               return fsm_local_search();
-       }
-
-       /* Normal FSM logic follows */
-
-       old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
-       search_cat = fsm_space_needed_to_cat(spaceNeeded);
 
        /* Get the location of the FSM byte representing the heap block */
        addr = fsm_get_location(oldPage, &slot);
@@ -263,41 +176,20 @@ RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage,
  * Note that if the new spaceAvail value is higher than the old value stored
  * in the FSM, the space might not become visible to searchers until the next
  * FreeSpaceMapVacuum call, which updates the upper level pages.
- *
- * Callers have no need for a local map.
  */
 void
-RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk,
-                                               Size spaceAvail, BlockNumber nblocks)
+RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail)
 {
-       int                     new_cat;
+       int                     new_cat = fsm_space_avail_to_cat(spaceAvail);
        FSMAddress      addr;
        uint16          slot;
-       BlockNumber dummy;
-
-       if (!fsm_allow_writes(rel, heapBlk, nblocks, &dummy))
-               /* No FSM to update and no local map either */
-               return;
 
        /* Get the location of the FSM byte representing the heap block */
        addr = fsm_get_location(heapBlk, &slot);
 
-       new_cat = fsm_space_avail_to_cat(spaceAvail);
        fsm_set_and_search(rel, addr, slot, new_cat, 0);
 }
 
-/*
- * Clear the local map.  We must call this when we have found a block with
- * enough free space, when we extend the relation, or on transaction abort.
- */
-void
-FSMClearLocalMap(void)
-{
-       fsm_local_map.nblocks = 0;
-       memset(&fsm_local_map.map, FSM_LOCAL_NOT_AVAIL,
-                  sizeof(fsm_local_map.map));
-}
-
 /*
  * XLogRecordPageWithFreeSpace - like RecordPageWithFreeSpace, for use in
  *             WAL replay
@@ -312,30 +204,6 @@ XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk,
        BlockNumber blkno;
        Buffer          buf;
        Page            page;
-       bool            write_to_fsm;
-
-       /* This is meant to mirror the logic in fsm_allow_writes() */
-       if (heapBlk >= HEAP_FSM_CREATION_THRESHOLD)
-               write_to_fsm = true;
-       else
-       {
-               /* Open the relation at smgr level */
-               SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
-
-               if (smgrexists(smgr, FSM_FORKNUM))
-                       write_to_fsm = true;
-               else
-               {
-                       BlockNumber heap_nblocks = smgrnblocks(smgr, MAIN_FORKNUM);
-                       if (heap_nblocks > HEAP_FSM_CREATION_THRESHOLD)
-                               write_to_fsm = true;
-                       else
-                               write_to_fsm = false;
-               }
-       }
-
-       if (!write_to_fsm)
-               return;
 
        /* Get the location of the FSM byte representing the heap block */
        addr = fsm_get_location(heapBlk, &slot);
@@ -1036,134 +904,3 @@ fsm_vacuum_page(Relation rel, FSMAddress addr,
 
        return max_avail;
 }
-
-/*
- * For heaps, we prevent creation of the FSM unless the number of pages
- * exceeds HEAP_FSM_CREATION_THRESHOLD.  For tables that don't already have
- * a FSM, this will save an inode and a few kB of space.
- *
- * XXX The API is a little awkward -- if the caller passes a valid nblocks
- * value, it can avoid invoking a system call.  If the caller passes
- * InvalidBlockNumber and receives a false return value, it can get an
- * up-to-date relation size from get_nblocks.  This saves a few cycles in
- * the caller, which would otherwise need to get the relation size by itself.
- */
-static bool
-fsm_allow_writes(Relation rel, BlockNumber heapblk,
-                                BlockNumber nblocks, BlockNumber *get_nblocks)
-{
-       bool            skip_get_nblocks;
-
-       if (heapblk >= HEAP_FSM_CREATION_THRESHOLD)
-               return true;
-
-       /* Non-heap rels can always create a FSM. */
-       if (rel->rd_rel->relkind != RELKIND_RELATION &&
-               rel->rd_rel->relkind != RELKIND_TOASTVALUE)
-               return true;
-
-       /*
-        * If the caller knows nblocks, we can avoid a system call later.
-        * If it doesn't, maybe we have relpages from a previous VACUUM.
-        * Since the table may have extended since then, we still have to
-        * count the pages later if we can't return now.
-        */
-       if (nblocks != InvalidBlockNumber)
-       {
-               if (nblocks > HEAP_FSM_CREATION_THRESHOLD)
-                       return true;
-               else
-                       skip_get_nblocks = true;
-       }
-       else
-       {
-               if (rel->rd_rel->relpages != InvalidBlockNumber &&
-                       rel->rd_rel->relpages > HEAP_FSM_CREATION_THRESHOLD)
-                       return true;
-               else
-                       skip_get_nblocks = false;
-       }
-
-       RelationOpenSmgr(rel);
-       if (smgrexists(rel->rd_smgr, FSM_FORKNUM))
-               return true;
-
-       if (skip_get_nblocks)
-               return false;
-
-       /* last resort */
-       *get_nblocks = RelationGetNumberOfBlocks(rel);
-       if (*get_nblocks > HEAP_FSM_CREATION_THRESHOLD)
-               return true;
-       else
-               return false;
-}
-
-/*
- * Initialize or update the local map of blocks to try, for when there is
- * no FSM.
- *
- * When we initialize the map, the whole heap is potentially available to
- * try.  Testing revealed that trying every block can cause a small
- * performance dip compared to when we use a FSM, so we try every other
- * block instead.
- */
-static void
-fsm_local_set(Relation rel, BlockNumber cur_nblocks)
-{
-       BlockNumber blkno,
-                               cached_target_block;
-
-       /* The local map must not be set already. */
-       Assert(!FSM_LOCAL_MAP_EXISTS);
-
-       /*
-        * Starting at the current last block in the relation and working
-        * backwards, mark alternating blocks as available.
-        */
-       blkno = cur_nblocks - 1;
-       while (true)
-       {
-               fsm_local_map.map[blkno] = FSM_LOCAL_AVAIL;
-               if (blkno >= 2)
-                       blkno -= 2;
-               else
-                       break;
-       }
-
-       /* Cache the number of blocks. */
-       fsm_local_map.nblocks = cur_nblocks;
-
-       /* Set the status of the cached target block to 'unavailable'. */
-       cached_target_block = RelationGetTargetBlock(rel);
-       if (cached_target_block != InvalidBlockNumber &&
-               cached_target_block < cur_nblocks)
-               fsm_local_map.map[cached_target_block] = FSM_LOCAL_NOT_AVAIL;
-}
-
-/*
- * Search the local map for an available block to try, in descending order.
- * As such, there is no heuristic available to decide which order will be
- * better to try, but the probability of having space in the last block in the
- * map is higher because that is the most recent block added to the heap.
- *
- * This function is used when there is no FSM.
- */
-static BlockNumber
-fsm_local_search(void)
-{
-       BlockNumber             target_block;
-
-       /* Local map must be set by now. */
-       Assert(FSM_LOCAL_MAP_EXISTS);
-
-       target_block = fsm_local_map.nblocks;
-       do
-       {
-               target_block--;
-               if (fsm_local_map.map[target_block] == FSM_LOCAL_AVAIL)
-                       return target_block;
-       } while (target_block > 0);
-
-       return InvalidBlockNumber;
-}
index 9d8f43d3739600e2a9a2ccd1e8e5cdcad931eb26..58cedeaa9f7e9b4f905d67d7ecb827dad6a3bfc3 100644 (file)
@@ -37,7 +37,7 @@
 BlockNumber
 GetFreeIndexPage(Relation rel)
 {
-       BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ / 2, true);
+       BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ / 2);
 
        if (blkno != InvalidBlockNumber)
                RecordUsedIndexPage(rel, blkno);
@@ -51,7 +51,7 @@ GetFreeIndexPage(Relation rel)
 void
 RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
 {
-       RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1, InvalidBlockNumber);
+       RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1);
 }
 
 
@@ -61,7 +61,7 @@ RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
 void
 RecordUsedIndexPage(Relation rel, BlockNumber usedBlock)
 {
-       RecordPageWithFreeSpace(rel, usedBlock, 0, InvalidBlockNumber);
+       RecordPageWithFreeSpace(rel, usedBlock, 0);
 }
 
 /*
index dbaae651c58c8ba93d9aad1428726def416d504c..8b0003343827ae8645d6c8db6c3f95c30e9affab 100644 (file)
 #include "storage/relfilenode.h"
 #include "utils/relcache.h"
 
-/* Only create the FSM if the heap has greater than this many blocks */
-#define HEAP_FSM_CREATION_THRESHOLD 4
-
 /* prototypes for public functions in freespace.c */
 extern Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk);
-extern BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded,
-                                        bool check_fsm_only);
+extern BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded);
 extern BlockNumber RecordAndGetPageWithFreeSpace(Relation rel,
                                                          BlockNumber oldPage,
                                                          Size oldSpaceAvail,
                                                          Size spaceNeeded);
 extern void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk,
-                                               Size spaceAvail, BlockNumber nblocks);
-extern void FSMClearLocalMap(void);
+                                               Size spaceAvail);
 extern void XLogRecordPageWithFreeSpace(RelFileNode rnode, BlockNumber heapBlk,
                                                        Size spaceAvail);
 
diff --git a/src/test/regress/expected/fsm.out b/src/test/regress/expected/fsm.out
deleted file mode 100644 (file)
index df6b15b..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
---
--- Free Space Map test
---
-CREATE TABLE fsm_check_size (num int, str text);
--- Fill 3 blocks with as many large records as will fit
--- No FSM
-INSERT INTO fsm_check_size SELECT i, rpad('', 1024, 'a')
-FROM generate_series(1,7*3) i;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'main') AS heap_size,
-pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
- heap_size | fsm_size 
------------+----------
-     24576 |        0
-(1 row)
-
--- Clear some space on block 0
-DELETE FROM fsm_check_size WHERE num <= 5;
-VACUUM fsm_check_size;
--- Insert small record in block 2 to set the cached smgr targetBlock
-INSERT INTO fsm_check_size VALUES(99, 'b');
--- Insert large record and make sure it goes in block 0 rather than
--- causing the relation to extend
-INSERT INTO fsm_check_size VALUES (101, rpad('', 1024, 'a'));
-SELECT pg_relation_size('fsm_check_size', 'main') AS heap_size,
-pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
- heap_size | fsm_size 
------------+----------
-     24576 |        0
-(1 row)
-
--- Extend table with enough blocks to exceed the FSM threshold
--- FSM is created and extended to 3 blocks
-INSERT INTO fsm_check_size SELECT i, 'c' FROM generate_series(200,1200) i;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
- fsm_size 
-----------
-    24576
-(1 row)
-
--- Truncate heap to 1 block
--- No change in FSM
-DELETE FROM fsm_check_size WHERE num > 7;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
- fsm_size 
-----------
-    24576
-(1 row)
-
--- Truncate heap to 0 blocks
--- FSM now truncated to 2 blocks
-DELETE FROM fsm_check_size;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
- fsm_size 
-----------
-    16384
-(1 row)
-
--- Add long random string to extend TOAST table to 1 block
-INSERT INTO fsm_check_size
-VALUES(0, (SELECT string_agg(md5(chr(i)), '')
-                  FROM generate_series(1,100) i));
-VACUUM fsm_check_size;
-SELECT pg_relation_size(reltoastrelid, 'main') AS toast_size,
-pg_relation_size(reltoastrelid, 'fsm') AS toast_fsm_size
-FROM pg_class WHERE relname = 'fsm_check_size';
- toast_size | toast_fsm_size 
-------------+----------------
-       8192 |              0
-(1 row)
-
-DROP TABLE fsm_check_size;
index 4051a4ad4e15b8a1122d92fd7e9201a0b1e6438b..cc0bbf5db9f0fbf143ab71c2db31ee325792e900 100644 (file)
@@ -68,12 +68,6 @@ test: create_aggregate create_function_3 create_cast constraints triggers inheri
 # ----------
 test: sanity_check
 
-# ----------
-# fsm does a delete followed by vacuum, and running it in parallel can prevent
-# removal of rows.
-# ----------
-test: fsm
-
 # ----------
 # Believe it or not, select creates a table, subsequent
 # tests need.
index ac1ea622d65997e5b1822faf77d69334626afacd..0c10c7100c6276d3ec727c736eb61afcda3f8242 100644 (file)
@@ -80,7 +80,6 @@ test: roleattributes
 test: create_am
 test: hash_func
 test: sanity_check
-test: fsm
 test: errors
 test: select
 test: select_into
diff --git a/src/test/regress/sql/fsm.sql b/src/test/regress/sql/fsm.sql
deleted file mode 100644 (file)
index 07f5055..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
---
--- Free Space Map test
---
-
-CREATE TABLE fsm_check_size (num int, str text);
-
--- Fill 3 blocks with as many large records as will fit
--- No FSM
-INSERT INTO fsm_check_size SELECT i, rpad('', 1024, 'a')
-FROM generate_series(1,7*3) i;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'main') AS heap_size,
-pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
-
--- Clear some space on block 0
-DELETE FROM fsm_check_size WHERE num <= 5;
-VACUUM fsm_check_size;
-
--- Insert small record in block 2 to set the cached smgr targetBlock
-INSERT INTO fsm_check_size VALUES(99, 'b');
-
--- Insert large record and make sure it goes in block 0 rather than
--- causing the relation to extend
-INSERT INTO fsm_check_size VALUES (101, rpad('', 1024, 'a'));
-SELECT pg_relation_size('fsm_check_size', 'main') AS heap_size,
-pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
-
--- Extend table with enough blocks to exceed the FSM threshold
--- FSM is created and extended to 3 blocks
-INSERT INTO fsm_check_size SELECT i, 'c' FROM generate_series(200,1200) i;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
-
--- Truncate heap to 1 block
--- No change in FSM
-DELETE FROM fsm_check_size WHERE num > 7;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
-
--- Truncate heap to 0 blocks
--- FSM now truncated to 2 blocks
-DELETE FROM fsm_check_size;
-VACUUM fsm_check_size;
-SELECT pg_relation_size('fsm_check_size', 'fsm') AS fsm_size;
-
--- Add long random string to extend TOAST table to 1 block
-INSERT INTO fsm_check_size
-VALUES(0, (SELECT string_agg(md5(chr(i)), '')
-                  FROM generate_series(1,100) i));
-VACUUM fsm_check_size;
-SELECT pg_relation_size(reltoastrelid, 'main') AS toast_size,
-pg_relation_size(reltoastrelid, 'fsm') AS toast_fsm_size
-FROM pg_class WHERE relname = 'fsm_check_size';
-
-DROP TABLE fsm_check_size;