Commit 15c121b3 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Rewrite the FSM. Instead of relying on a fixed-size shared memory segment, the

free space information is stored in a dedicated FSM relation fork, with each
relation (except for hash indexes; they don't use FSM).

This eliminates the max_fsm_relations and max_fsm_pages GUC options; remove any
trace of them from the backend, initdb, and documentation.

Rewrite contrib/pg_freespacemap to match the new FSM implementation. Also
introduce a new variant of the get_raw_page(regclass, int4, int4) function in
contrib/pageinspect that let's you to return pages from any relation fork, and
a new fsm_page_contents() function to inspect the new FSM pages.
parent 2dbc0ca9
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
# #
# pageinspect Makefile # pageinspect Makefile
# #
# $PostgreSQL: pgsql/contrib/pageinspect/Makefile,v 1.3 2007/11/10 23:59:51 momjian Exp $ # $PostgreSQL: pgsql/contrib/pageinspect/Makefile,v 1.4 2008/09/30 10:52:09 heikki Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
MODULE_big = pageinspect MODULE_big = pageinspect
OBJS = rawpage.o heapfuncs.o btreefuncs.o OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o
DATA_built = pageinspect.sql DATA_built = pageinspect.sql
DATA = uninstall_pageinspect.sql DATA = uninstall_pageinspect.sql
......
/*-------------------------------------------------------------------------
*
* fsmfuncs.c
* Functions to investigate FSM pages
*
* These functions are restricted to superusers for the fear of introducing
* security holes if the input checking isn't as water-tight as it should.
* You'd need to be superuser to obtain a raw page image anyway, so
* there's hardly any use case for using these without superuser-rights
* anyway.
*
* Copyright (c) 2007-2008, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/pageinspect/fsmfuncs.c,v 1.1 2008/09/30 10:52:09 heikki Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "lib/stringinfo.h"
#include "storage/fsm_internals.h"
#include "utils/builtins.h"
#include "miscadmin.h"
#include "funcapi.h"
Datum fsm_page_contents(PG_FUNCTION_ARGS);
/*
* Dumps the contents of a FSM page.
*/
PG_FUNCTION_INFO_V1(fsm_page_contents);
Datum
fsm_page_contents(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
int raw_page_size;
StringInfoData sinfo;
FSMPage fsmpage;
int i;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to use raw page functions"))));
raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
fsmpage = (FSMPage) PageGetContents(VARDATA(raw_page));
initStringInfo(&sinfo);
for(i=0; i < NodesPerPage; i++)
{
if (fsmpage->fp_nodes[i] != 0)
appendStringInfo(&sinfo, "%d: %d\n", i, fsmpage->fp_nodes[i]);
}
appendStringInfo(&sinfo, "fp_next_slot: %d\n", fsmpage->fp_next_slot);
PG_RETURN_TEXT_P(cstring_to_text(sinfo.data));
}
/* $PostgreSQL: pgsql/contrib/pageinspect/pageinspect.sql.in,v 1.4 2007/11/13 04:24:28 momjian Exp $ */ /* $PostgreSQL: pgsql/contrib/pageinspect/pageinspect.sql.in,v 1.5 2008/09/30 10:52:09 heikki Exp $ */
-- Adjust this setting to control where the objects get created. -- Adjust this setting to control where the objects get created.
SET search_path = public; SET search_path = public;
...@@ -6,11 +6,16 @@ SET search_path = public; ...@@ -6,11 +6,16 @@ SET search_path = public;
-- --
-- get_raw_page() -- get_raw_page()
-- --
CREATE OR REPLACE FUNCTION get_raw_page(text, int4) CREATE OR REPLACE FUNCTION get_raw_page(text, int4, int4)
RETURNS bytea RETURNS bytea
AS 'MODULE_PATHNAME', 'get_raw_page' AS 'MODULE_PATHNAME', 'get_raw_page'
LANGUAGE C STRICT; LANGUAGE C STRICT;
CREATE OR REPLACE FUNCTION get_raw_page(text, int4)
RETURNS bytea
AS $$ SELECT get_raw_page($1, 0, $2); $$
LANGUAGE SQL STRICT;
-- --
-- page_header() -- page_header()
-- --
...@@ -92,3 +97,11 @@ CREATE OR REPLACE FUNCTION bt_page_items(IN relname text, IN blkno int4, ...@@ -92,3 +97,11 @@ CREATE OR REPLACE FUNCTION bt_page_items(IN relname text, IN blkno int4,
RETURNS SETOF record RETURNS SETOF record
AS 'MODULE_PATHNAME', 'bt_page_items' AS 'MODULE_PATHNAME', 'bt_page_items'
LANGUAGE C STRICT; LANGUAGE C STRICT;
--
-- fsm_page_contents()
--
CREATE OR REPLACE FUNCTION fsm_page_contents(IN page bytea)
RETURNS text
AS 'MODULE_PATHNAME', 'fsm_page_contents'
LANGUAGE C STRICT;
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Copyright (c) 2007-2008, PostgreSQL Global Development Group * Copyright (c) 2007-2008, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/contrib/pageinspect/rawpage.c,v 1.6 2008/05/12 00:00:43 alvherre Exp $ * $PostgreSQL: pgsql/contrib/pageinspect/rawpage.c,v 1.7 2008/09/30 10:52:09 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -41,7 +41,8 @@ Datum ...@@ -41,7 +41,8 @@ Datum
get_raw_page(PG_FUNCTION_ARGS) get_raw_page(PG_FUNCTION_ARGS)
{ {
text *relname = PG_GETARG_TEXT_P(0); text *relname = PG_GETARG_TEXT_P(0);
uint32 blkno = PG_GETARG_UINT32(1); uint32 forknum = PG_GETARG_UINT32(1);
uint32 blkno = PG_GETARG_UINT32(2);
Relation rel; Relation rel;
RangeVar *relrv; RangeVar *relrv;
...@@ -54,6 +55,11 @@ get_raw_page(PG_FUNCTION_ARGS) ...@@ -54,6 +55,11 @@ get_raw_page(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to use raw functions")))); (errmsg("must be superuser to use raw functions"))));
if (forknum > MAX_FORKNUM)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid fork number")));
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
rel = relation_openrv(relrv, AccessShareLock); rel = relation_openrv(relrv, AccessShareLock);
...@@ -80,7 +86,7 @@ get_raw_page(PG_FUNCTION_ARGS) ...@@ -80,7 +86,7 @@ get_raw_page(PG_FUNCTION_ARGS)
/* Take a verbatim copy of the page */ /* Take a verbatim copy of the page */
buf = ReadBuffer(rel, blkno); buf = ReadBufferWithFork(rel, forknum, blkno);
LockBuffer(buf, BUFFER_LOCK_SHARE); LockBuffer(buf, BUFFER_LOCK_SHARE);
memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ); memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
......
/* $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.sql.in,v 1.8 2007/11/13 04:24:28 momjian Exp $ */ /* $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.sql.in,v 1.9 2008/09/30 10:52:09 heikki Exp $ */
-- Adjust this setting to control where the objects get created. -- Adjust this setting to control where the objects get created.
SET search_path = public; SET search_path = public;
-- Register the functions. -- Register the C function.
CREATE OR REPLACE FUNCTION pg_freespacemap_pages() CREATE OR REPLACE FUNCTION pg_freespace(regclass, int4)
RETURNS SETOF RECORD RETURNS int2
AS 'MODULE_PATHNAME', 'pg_freespacemap_pages' AS 'MODULE_PATHNAME', 'pg_freespace'
LANGUAGE C; LANGUAGE C;
CREATE OR REPLACE FUNCTION pg_freespacemap_relations() -- pg_freespace shows the recorded space avail at each block in a relation
CREATE OR REPLACE FUNCTION
pg_freespace(rel regclass, blkno OUT int4, avail OUT int2)
RETURNS SETOF RECORD RETURNS SETOF RECORD
AS 'MODULE_PATHNAME', 'pg_freespacemap_relations' AS $$
LANGUAGE C; SELECT blkno::int4, pg_freespace($1, blkno::int4) AS avail
FROM generate_series(0, pg_relation_size($1) / current_setting('block_size')::bigint - 1) AS blkno;
$$
LANGUAGE SQL;
-- Create views for convenient access.
CREATE VIEW pg_freespacemap_pages AS
SELECT P.* FROM pg_freespacemap_pages() AS P
(reltablespace oid,
reldatabase oid,
relfilenode oid,
relblocknumber bigint,
bytes integer);
CREATE VIEW pg_freespacemap_relations AS
SELECT P.* FROM pg_freespacemap_relations() AS P
(reltablespace oid,
reldatabase oid,
relfilenode oid,
avgrequest integer,
interestingpages integer,
storedpages integer,
nextpage integer);
-- Don't want these to be available to public. -- Don't want these to be available to public.
REVOKE ALL ON FUNCTION pg_freespacemap_pages() FROM PUBLIC; REVOKE ALL ON FUNCTION pg_freespace(regclass, int4) FROM PUBLIC;
REVOKE ALL ON pg_freespacemap_pages FROM PUBLIC; REVOKE ALL ON FUNCTION pg_freespace(regclass) FROM PUBLIC;
REVOKE ALL ON FUNCTION pg_freespacemap_relations() FROM PUBLIC;
REVOKE ALL ON pg_freespacemap_relations FROM PUBLIC;
<!-- $PostgreSQL: pgsql/doc/src/sgml/acronyms.sgml,v 1.5 2008/03/18 16:05:07 mha Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/acronyms.sgml,v 1.6 2008/09/30 10:52:09 heikki Exp $ -->
<appendix id="acronyms"> <appendix id="acronyms">
<title>Acronyms</title> <title>Acronyms</title>
...@@ -216,7 +216,7 @@ ...@@ -216,7 +216,7 @@
<term><acronym>FSM</acronym></term> <term><acronym>FSM</acronym></term>
<listitem> <listitem>
<para> <para>
<link linkend="runtime-config-resource-fsm">Free Space Map</link> <link linkend="storage-fsm">Free Space Map</link>
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.190 2008/08/25 19:03:37 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.191 2008/09/30 10:52:09 heikki Exp $ -->
<chapter Id="runtime-config"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
...@@ -896,80 +896,6 @@ SET ENABLE_SEQSCAN TO OFF; ...@@ -896,80 +896,6 @@ SET ENABLE_SEQSCAN TO OFF;
</varlistentry> </varlistentry>
</variablelist> </variablelist>
</sect2>
<sect2 id="runtime-config-resource-fsm">
<title>Free Space Map</title>
<indexterm>
<primary>free space map</primary>
</indexterm>
<para>
These parameters control the size of the shared <firstterm>free space
map</> (<acronym>FSM</>), which tracks the locations of unused space in the database.
An undersized free space map can cause the database to consume
increasing amounts of disk space over time, because free space that
is not in the map cannot be re-used; instead <productname>PostgreSQL</>
will request more disk space from the operating system when it needs
to store new data.
The last few lines displayed by a database-wide <command>VACUUM VERBOSE</>
command can help in determining if the current settings are adequate.
A <literal>NOTICE</> message is also printed during such an operation
if the current settings are too low.
</para>
<para>
Increasing these parameters might cause <productname>PostgreSQL</>
to request more <systemitem class="osname">System V</> shared
memory than your operating system's default configuration
allows. See <xref linkend="sysvipc"> for information on how to
adjust those parameters, if necessary.
</para>
<variablelist>
<varlistentry id="guc-max-fsm-pages" xreflabel="max_fsm_pages">
<term><varname>max_fsm_pages</varname> (<type>integer</type>)</term>
<indexterm>
<primary><varname>max_fsm_pages</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Sets the maximum number of disk pages for which free space will
be tracked in the shared free-space map. Six bytes of shared memory
are consumed for each page slot. This setting must be at least
16 * <varname>max_fsm_relations</varname>. The default is chosen
by <application>initdb</> depending on the amount of available memory,
and can range from 20k to 200k pages.
This parameter can only be set at server start.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-max-fsm-relations" xreflabel="max_fsm_relations">
<term><varname>max_fsm_relations</varname> (<type>integer</type>)</term>
<indexterm>
<primary><varname>max_fsm_relations</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Sets the maximum number of relations (tables and indexes) for which
free space will be tracked in the shared free-space map. Roughly
seventy bytes of shared memory are consumed for each slot.
The default is one thousand relations.
This parameter can only be set at server start.
</para>
</listitem>
</varlistentry>
</variablelist>
<note>
<para>
See the <xref linkend="sql-vacuum" endterm="sql-vacuum-title">
command for information on setting this parameter.
</para>
</note>
</sect2> </sect2>
<sect2 id="runtime-config-resource-kernel"> <sect2 id="runtime-config-resource-kernel">
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/pageinspect.sgml,v 1.3 2007/12/10 05:32:51 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/pageinspect.sgml,v 1.4 2008/09/30 10:52:09 heikki Exp $ -->
<sect1 id="pageinspect"> <sect1 id="pageinspect">
<title>pageinspect</title> <title>pageinspect</title>
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term> <term>
<function>get_raw_page(text, int) returns bytea</function> <function>get_raw_page(relname text, forknum int, blkno int) returns bytea</function>
</term> </term>
<listitem> <listitem>
...@@ -27,13 +27,28 @@ ...@@ -27,13 +27,28 @@
<function>get_raw_page</function> reads the specified block of the named <function>get_raw_page</function> reads the specified block of the named
table and returns a copy as a <type>bytea</> value. This allows a table and returns a copy as a <type>bytea</> value. This allows a
single time-consistent copy of the block to be obtained. single time-consistent copy of the block to be obtained.
<literal>forknum</literal> should be 0 for the main data fork, or 1 for
the FSM.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<function>page_header(bytea) returns record</function> <function>get_raw_page(relname text, blkno int) returns bytea</function>
</term>
<listitem>
<para>
A shorthand of above, for reading from the main fork. Equal to
<literal>get_raw_page(relname, 0, blkno)</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<function>page_header(page bytea) returns record</function>
</term> </term>
<listitem> <listitem>
...@@ -63,7 +78,7 @@ test=# SELECT * FROM page_header(get_raw_page('pg_class', 0)); ...@@ -63,7 +78,7 @@ test=# SELECT * FROM page_header(get_raw_page('pg_class', 0));
<varlistentry> <varlistentry>
<term> <term>
<function>heap_page_items(bytea) returns setof record</function> <function>heap_page_items(page bytea) returns setof record</function>
</term> </term>
<listitem> <listitem>
...@@ -90,7 +105,7 @@ test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0)); ...@@ -90,7 +105,7 @@ test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0));
<varlistentry> <varlistentry>
<term> <term>
<function>bt_metap(text) returns record</function> <function>bt_metap(relname text) returns record</function>
</term> </term>
<listitem> <listitem>
...@@ -113,7 +128,7 @@ fastlevel | 0 ...@@ -113,7 +128,7 @@ fastlevel | 0
<varlistentry> <varlistentry>
<term> <term>
<function>bt_page_stats(text, int) returns record</function> <function>bt_page_stats(relname text, blkno int) returns record</function>
</term> </term>
<listitem> <listitem>
...@@ -141,7 +156,7 @@ btpo_flags | 3 ...@@ -141,7 +156,7 @@ btpo_flags | 3
<varlistentry> <varlistentry>
<term> <term>
<function>bt_page_items(text, int) returns setof record</function> <function>bt_page_items(relname text, blkno int) returns setof record</function>
</term> </term>
<listitem> <listitem>
...@@ -164,6 +179,26 @@ test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1); ...@@ -164,6 +179,26 @@ test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1);
</programlisting> </programlisting>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<function>fsm_page_contents(page bytea) returns text</function>
</term>
<listitem>
<para>
<function>fsm_page_contents</function> shows the internal node structure
of a FSM page. The output is a multi-line string, with one line per
node in the binary tree within the page. Only those nodes that are not
zero are printed. The so-called "next" pointer, which points to the
next slot to be returned from the page, is also printed.
</para>
<para>
See <filename>src/backend/storage/freespace/README</> for more
information on the structure of an FSM page.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</sect2> </sect2>
......
This diff is collapsed.
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.51 2008/02/03 16:24:08 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.52 2008/09/30 10:52:10 heikki Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -96,11 +96,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER"> ...@@ -96,11 +96,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
<term><literal>VERBOSE</literal></term> <term><literal>VERBOSE</literal></term>
<listitem> <listitem>
<para> <para>
Prints a detailed vacuum activity report for each table. Can be used Prints a detailed vacuum activity report for each table.
to help determine appropriate settings for
<xref linkend="guc-max-fsm-pages">,
<xref linkend="guc-max-fsm-relations">, and
<xref linkend="guc-default-statistics-target">.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/release.sgml,v 1.585 2008/09/17 20:57:35 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/release.sgml,v 1.586 2008/09/30 10:52:09 heikki Exp $ -->
<!-- <!--
Typical markup: Typical markup:
...@@ -6004,8 +6004,7 @@ current_date &lt; 2017-11-17 ...@@ -6004,8 +6004,7 @@ current_date &lt; 2017-11-17
<para> <para>
Increase default values for <link Increase default values for <link
linkend="guc-shared-buffers"><varname>shared_buffers</></link> linkend="guc-shared-buffers"><varname>shared_buffers</></link>
and <link and <varname>max_fsm_pages</>
linkend="guc-max-fsm-pages"><varname>max_fsm_pages</></link>
(Andrew) (Andrew)
</para> </para>
</listitem> </listitem>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.417 2008/09/23 09:20:34 heikki Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.418 2008/09/30 10:52:10 heikki Exp $ -->
<chapter Id="runtime"> <chapter Id="runtime">
<title>Operating System Environment</title> <title>Operating System Environment</title>
...@@ -1117,16 +1117,6 @@ set semsys:seminfo_semmsl=32 ...@@ -1117,16 +1117,6 @@ set semsys:seminfo_semmsl=32
<entry>8200 (assuming 8 kB <symbol>XLOG_BLCKSZ</>)</entry> <entry>8200 (assuming 8 kB <symbol>XLOG_BLCKSZ</>)</entry>
</row> </row>
<row>
<entry><xref linkend="guc-max-fsm-relations"></>
<entry>70</>
</row>
<row>
<entry><xref linkend="guc-max-fsm-pages"></>
<entry>6</>
</row>
<row> <row>
<entry>Fixed space requirements</> <entry>Fixed space requirements</>
<entry>770 kB</entry> <entry>770 kB</entry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.24 2008/08/05 12:09:30 mha Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.25 2008/09/30 10:52:10 heikki Exp $ -->
<chapter id="storage"> <chapter id="storage">
...@@ -130,7 +130,12 @@ there. ...@@ -130,7 +130,12 @@ there.
<para> <para>
Each table and index is stored in a separate file, named after the table Each table and index is stored in a separate file, named after the table
or index's <firstterm>filenode</> number, which can be found in or index's <firstterm>filenode</> number, which can be found in
<structname>pg_class</>.<structfield>relfilenode</>. <structname>pg_class</>.<structfield>relfilenode</>. In addition to the
main file (aka. main fork), a <firstterm>free space map</> (see
<xref linkend="storage-fsm">) that stores information about free space
available in the relation, is stored in a file named after the filenode
number, with the the _1 suffix. For example, if the table's filenode number
is 12345, the FSM file is named <filename>12345_1</>.
</para> </para>
<caution> <caution>
...@@ -367,6 +372,48 @@ comparison table, in which all the HTML pages were cut down to 7 kB to fit. ...@@ -367,6 +372,48 @@ comparison table, in which all the HTML pages were cut down to 7 kB to fit.
</sect1> </sect1>
<sect1 id="storage-fsm">
<title>Free Space Map</title>
<indexterm>
<primary>Free Space Map</primary>
</indexterm>
<indexterm><primary>FSM</><see>Free Space Map</></indexterm>
<para>
A Free Space Map is stored with every heap and index relation, except for
hash indexes, to keep track of available space in the relation. It's stored
along the main relation data, in a separate FSM relation fork, named after
relfilenode of the relation, but with a <literal>_1</> suffix. For example,
if the relfilenode of a relation is 12345, the FSM is stored in a file called
<filename>12345_1</>, in the same directory as the main relation file.
</para>
<para>
The Free Space Map is organized as a tree of <acronym>FSM</> pages. The
bottom level <acronym>FSM</> pages stores the free space available on every
heap (or index) page, using one byte to represent each heap page. The upper
levels aggregate information from the lower levels.
</para>
<para>
Within each <acronym>FSM</> page is a binary tree, stored in an array with
one byte per node. Each leaf node represents a heap page, or a lower level
<acronym>FSM</> page. In each non-leaf node, the higher of its children's
values is stored. The maximum value in the leaf nodes is therefore stored
at the root.
</para>
<para>
See <filename>src/backend/storage/freespace/README</> for more details on
how the <acronym>FSM</> is structured, and how it's updated and searched.
<xref linkend="pgfreespacemap"> contrib module can be used to view the
information stored in free space maps.
</para>
</sect1>
<sect1 id="storage-page-layout"> <sect1 id="storage-page-layout">
<title>Database Page Layout</title> <title>Database Page Layout</title>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.14 2008/07/11 21:06:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.15 2008/09/30 10:52:10 heikki Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "catalog/index.h" #include "catalog/index.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -283,6 +284,9 @@ ginbuild(PG_FUNCTION_ARGS) ...@@ -283,6 +284,9 @@ ginbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data", elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index)); RelationGetRelationName(index));
/* Initialize FSM */
InitIndexFreeSpaceMap(index);
initGinState(&buildstate.ginstate, index); initGinState(&buildstate.ginstate, index);
/* initialize the root page */ /* initialize the root page */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.16 2008/07/11 21:06:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.17 2008/09/30 10:52:10 heikki Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
void void
...@@ -151,7 +152,7 @@ GinNewBuffer(Relation index) ...@@ -151,7 +152,7 @@ GinNewBuffer(Relation index)
/* First, try to get a page from FSM */ /* First, try to get a page from FSM */
for (;;) for (;;)
{ {
BlockNumber blkno = GetFreeIndexPage(&index->rd_node); BlockNumber blkno = GetFreeIndexPage(index);
if (blkno == InvalidBlockNumber) if (blkno == InvalidBlockNumber)
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.21 2008/07/11 21:06:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.22 2008/09/30 10:52:10 heikki Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
typedef struct typedef struct
...@@ -678,10 +679,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -678,10 +679,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
bool needLock; bool needLock;
BlockNumber npages, BlockNumber npages,
blkno; blkno;
BlockNumber totFreePages, BlockNumber totFreePages;
nFreePages,
*freePages,
maxFreePages;
BlockNumber lastBlock = GIN_ROOT_BLKNO, BlockNumber lastBlock = GIN_ROOT_BLKNO,
lastFilledBlock = GIN_ROOT_BLKNO; lastFilledBlock = GIN_ROOT_BLKNO;
...@@ -711,12 +709,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -711,12 +709,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
if (needLock) if (needLock)
UnlockRelationForExtension(index, ExclusiveLock); UnlockRelationForExtension(index, ExclusiveLock);
maxFreePages = npages; totFreePages = 0;
if (maxFreePages > MaxFSMPages)
maxFreePages = MaxFSMPages;
totFreePages = nFreePages = 0;
freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++) for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++)
{ {
...@@ -731,8 +724,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -731,8 +724,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
if (GinPageIsDeleted(page)) if (GinPageIsDeleted(page))
{ {
if (nFreePages < maxFreePages) RecordFreeIndexPage(index, blkno);
freePages[nFreePages++] = blkno;
totFreePages++; totFreePages++;
} }
else else
...@@ -742,25 +734,16 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -742,25 +734,16 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
} }
lastBlock = npages - 1; lastBlock = npages - 1;
if (info->vacuum_full && nFreePages > 0) if (info->vacuum_full && lastBlock > lastFilledBlock)
{ {
/* try to truncate index */ /* try to truncate index */
int i; FreeSpaceMapTruncateRel(index, lastFilledBlock + 1);
RelationTruncate(index, lastFilledBlock + 1);
for (i = 0; i < nFreePages; i++)
if (freePages[i] >= lastFilledBlock)
{
totFreePages = nFreePages = i;
break;
}
if (lastBlock > lastFilledBlock)
RelationTruncate(index, lastFilledBlock + 1);
stats->pages_removed = lastBlock - lastFilledBlock; stats->pages_removed = lastBlock - lastFilledBlock;
totFreePages = totFreePages - stats->pages_removed;
} }
RecordIndexFreeSpace(&index->rd_node, totFreePages, nFreePages, freePages);
stats->pages_free = totFreePages; stats->pages_free = totFreePages;
if (needLock) if (needLock)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.151 2008/06/12 09:12:29 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.152 2008/09/30 10:52:10 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "catalog/index.h" #include "catalog/index.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "utils/memutils.h" #include "utils/memutils.h"
const XLogRecPtr XLogRecPtrForTemp = {1, 1}; const XLogRecPtr XLogRecPtrForTemp = {1, 1};
...@@ -102,6 +103,9 @@ gistbuild(PG_FUNCTION_ARGS) ...@@ -102,6 +103,9 @@ gistbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data", elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index)); RelationGetRelationName(index));
/* Initialize FSM */
InitIndexFreeSpaceMap(index);
/* no locking is needed */ /* no locking is needed */
initGISTstate(&buildstate.giststate, index); initGISTstate(&buildstate.giststate, index);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.30 2008/07/13 20:45:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.31 2008/09/30 10:52:10 heikki Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "access/gist_private.h" #include "access/gist_private.h"
#include "access/reloptions.h" #include "access/reloptions.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/rel.h" #include "utils/rel.h"
...@@ -617,7 +618,7 @@ gistNewBuffer(Relation r) ...@@ -617,7 +618,7 @@ gistNewBuffer(Relation r)
/* First, try to get a page from FSM */ /* First, try to get a page from FSM */
for (;;) for (;;)
{ {
BlockNumber blkno = GetFreeIndexPage(&r->rd_node); BlockNumber blkno = GetFreeIndexPage(r);
if (blkno == InvalidBlockNumber) if (blkno == InvalidBlockNumber)
break; /* nothing left in FSM */ break; /* nothing left in FSM */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.36 2008/06/12 09:12:30 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.37 2008/09/30 10:52:10 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -518,10 +519,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -518,10 +519,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
Relation rel = info->index; Relation rel = info->index;
BlockNumber npages, BlockNumber npages,
blkno; blkno;
BlockNumber totFreePages, BlockNumber totFreePages;
nFreePages,
*freePages,
maxFreePages;
BlockNumber lastBlock = GIST_ROOT_BLKNO, BlockNumber lastBlock = GIST_ROOT_BLKNO,
lastFilledBlock = GIST_ROOT_BLKNO; lastFilledBlock = GIST_ROOT_BLKNO;
bool needLock; bool needLock;
...@@ -589,13 +587,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -589,13 +587,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
if (needLock) if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock); UnlockRelationForExtension(rel, ExclusiveLock);
maxFreePages = npages; totFreePages = 0;
if (maxFreePages > MaxFSMPages)
maxFreePages = MaxFSMPages;
totFreePages = nFreePages = 0;
freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++) for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
{ {
Buffer buffer; Buffer buffer;
...@@ -609,9 +601,8 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -609,9 +601,8 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
if (PageIsNew(page) || GistPageIsDeleted(page)) if (PageIsNew(page) || GistPageIsDeleted(page))
{ {
if (nFreePages < maxFreePages)
freePages[nFreePages++] = blkno;
totFreePages++; totFreePages++;
RecordFreeIndexPage(rel, blkno);
} }
else else
lastFilledBlock = blkno; lastFilledBlock = blkno;
...@@ -619,25 +610,15 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -619,25 +610,15 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
} }
lastBlock = npages - 1; lastBlock = npages - 1;
if (info->vacuum_full && nFreePages > 0) if (info->vacuum_full && lastFilledBlock < lastBlock)
{ /* try to truncate index */ { /* try to truncate index */
int i; FreeSpaceMapTruncateRel(rel, lastFilledBlock + 1);
RelationTruncate(rel, lastFilledBlock + 1);
for (i = 0; i < nFreePages; i++)
if (freePages[i] >= lastFilledBlock)
{
totFreePages = nFreePages = i;
break;
}
if (lastBlock > lastFilledBlock)
RelationTruncate(rel, lastFilledBlock + 1);
stats->std.pages_removed = lastBlock - lastFilledBlock; stats->std.pages_removed = lastBlock - lastFilledBlock;
totFreePages = totFreePages - stats->std.pages_removed;
} }
RecordIndexFreeSpace(&rel->rd_node, totFreePages, nFreePages, freePages);
pfree(freePages);
/* return statistics */ /* return statistics */
stats->std.pages_free = totFreePages; stats->std.pages_free = totFreePages;
if (needLock) if (needLock)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.263 2008/09/11 14:01:09 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.264 2008/09/30 10:52:10 heikki Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -4721,6 +4721,9 @@ heap_sync(Relation rel) ...@@ -4721,6 +4721,9 @@ heap_sync(Relation rel)
/* FlushRelationBuffers will have opened rd_smgr */ /* FlushRelationBuffers will have opened rd_smgr */
smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM); smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
/* sync FSM as well */
smgrimmedsync(rel->rd_smgr, FSM_FORKNUM);
/* toast heap, if any */ /* toast heap, if any */
if (OidIsValid(rel->rd_rel->reltoastrelid)) if (OidIsValid(rel->rd_rel->reltoastrelid))
{ {
...@@ -4729,6 +4732,7 @@ heap_sync(Relation rel) ...@@ -4729,6 +4732,7 @@ heap_sync(Relation rel)
toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock); toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
FlushRelationBuffers(toastrel); FlushRelationBuffers(toastrel);
smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM); smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
smgrimmedsync(toastrel->rd_smgr, FSM_FORKNUM);
heap_close(toastrel, AccessShareLock); heap_close(toastrel, AccessShareLock);
} }
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.72 2008/07/13 20:45:47 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.73 2008/09/30 10:52:10 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -163,8 +163,7 @@ RelationGetBufferForTuple(Relation relation, Size len, ...@@ -163,8 +163,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
* We have no cached target page, so ask the FSM for an initial * We have no cached target page, so ask the FSM for an initial
* target. * target.
*/ */
targetBlock = GetPageWithFreeSpace(&relation->rd_node, targetBlock = GetPageWithFreeSpace(relation, len + saveFreeSpace);
len + saveFreeSpace);
/* /*
* If the FSM knows nothing of the rel, try the last page before we * If the FSM knows nothing of the rel, try the last page before we
...@@ -250,7 +249,7 @@ RelationGetBufferForTuple(Relation relation, Size len, ...@@ -250,7 +249,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
* Update FSM as to condition of this page, and ask for another page * Update FSM as to condition of this page, and ask for another page
* to try. * to try.
*/ */
targetBlock = RecordAndGetPageWithFreeSpace(&relation->rd_node, targetBlock = RecordAndGetPageWithFreeSpace(relation,
targetBlock, targetBlock,
pageFreeSpace, pageFreeSpace,
len + saveFreeSpace); len + saveFreeSpace);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.110 2008/07/13 20:45:47 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.111 2008/09/30 10:52:10 heikki Exp $
* *
* NOTES * NOTES
* Postgres btree pages look like ordinary relation pages. The opaque * Postgres btree pages look like ordinary relation pages. The opaque
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/inval.h" #include "utils/inval.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
...@@ -501,7 +502,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access) ...@@ -501,7 +502,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
*/ */
for (;;) for (;;)
{ {
blkno = GetFreeIndexPage(&rel->rd_node); blkno = GetFreeIndexPage(rel);
if (blkno == InvalidBlockNumber) if (blkno == InvalidBlockNumber)
break; break;
buf = ReadBuffer(rel, blkno); buf = ReadBuffer(rel, blkno);
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.161 2008/06/19 00:46:03 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.162 2008/09/30 10:52:10 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -56,9 +57,7 @@ typedef struct ...@@ -56,9 +57,7 @@ typedef struct
IndexBulkDeleteCallback callback; IndexBulkDeleteCallback callback;
void *callback_state; void *callback_state;
BTCycleId cycleid; BTCycleId cycleid;
BlockNumber *freePages; BlockNumber lastUsedPage;
int nFreePages; /* number of entries in freePages[] */
int maxFreePages; /* allocated size of freePages[] */
BlockNumber totFreePages; /* true total # of free pages */ BlockNumber totFreePages; /* true total # of free pages */
MemoryContext pagedelcontext; MemoryContext pagedelcontext;
} BTVacState; } BTVacState;
...@@ -110,6 +109,9 @@ btbuild(PG_FUNCTION_ARGS) ...@@ -110,6 +109,9 @@ btbuild(PG_FUNCTION_ARGS)
elog(ERROR, "index \"%s\" already contains data", elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index)); RelationGetRelationName(index));
/* Initialize FSM */
InitIndexFreeSpaceMap(index);
buildstate.spool = _bt_spoolinit(index, indexInfo->ii_Unique, false); buildstate.spool = _bt_spoolinit(index, indexInfo->ii_Unique, false);
/* /*
...@@ -623,9 +625,7 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, ...@@ -623,9 +625,7 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
vstate.callback = callback; vstate.callback = callback;
vstate.callback_state = callback_state; vstate.callback_state = callback_state;
vstate.cycleid = cycleid; vstate.cycleid = cycleid;
vstate.freePages = NULL; /* temporarily */ vstate.lastUsedPage = BTREE_METAPAGE;
vstate.nFreePages = 0;
vstate.maxFreePages = 0;
vstate.totFreePages = 0; vstate.totFreePages = 0;
/* Create a temporary memory context to run _bt_pagedel in */ /* Create a temporary memory context to run _bt_pagedel in */
...@@ -670,17 +670,6 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, ...@@ -670,17 +670,6 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
if (needLock) if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock); UnlockRelationForExtension(rel, ExclusiveLock);
/* Allocate freePages after we read num_pages the first time */
if (vstate.freePages == NULL)
{
/* No point in remembering more than MaxFSMPages pages */
vstate.maxFreePages = MaxFSMPages;
if ((BlockNumber) vstate.maxFreePages > num_pages)
vstate.maxFreePages = (int) num_pages;
vstate.freePages = (BlockNumber *)
palloc(vstate.maxFreePages * sizeof(BlockNumber));
}
/* Quit if we've scanned the whole relation */ /* Quit if we've scanned the whole relation */
if (blkno >= num_pages) if (blkno >= num_pages)
break; break;
...@@ -697,42 +686,22 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, ...@@ -697,42 +686,22 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
* acquiring exclusive lock on the index and then rechecking all the * acquiring exclusive lock on the index and then rechecking all the
* pages; doesn't seem worth it. * pages; doesn't seem worth it.
*/ */
if (info->vacuum_full && vstate.nFreePages > 0) if (info->vacuum_full && vstate.lastUsedPage < num_pages - 1)
{ {
BlockNumber new_pages = num_pages; BlockNumber new_pages = vstate.lastUsedPage + 1;
while (vstate.nFreePages > 0 &&
vstate.freePages[vstate.nFreePages - 1] == new_pages - 1)
{
new_pages--;
stats->pages_deleted--;
vstate.nFreePages--;
vstate.totFreePages = vstate.nFreePages; /* can't be more */
}
if (new_pages != num_pages)
{
/*
* Okay to truncate.
*/
RelationTruncate(rel, new_pages);
/* update statistics */ /*
stats->pages_removed += num_pages - new_pages; * Okay to truncate.
*/
FreeSpaceMapTruncateRel(rel, new_pages);
RelationTruncate(rel, new_pages);
num_pages = new_pages; /* update statistics */
} stats->pages_removed += num_pages - new_pages;
vstate.totFreePages -= (num_pages - new_pages);
num_pages = new_pages;
} }
/*
* Update the shared Free Space Map with the info we now have about free
* pages in the index, discarding any old info the map may have. We do not
* need to sort the page numbers; they're in order already.
*/
RecordIndexFreeSpace(&rel->rd_node, vstate.totFreePages,
vstate.nFreePages, vstate.freePages);
pfree(vstate.freePages);
MemoryContextDelete(vstate.pagedelcontext); MemoryContextDelete(vstate.pagedelcontext);
/* update statistics */ /* update statistics */
...@@ -788,8 +757,7 @@ restart: ...@@ -788,8 +757,7 @@ restart:
/* /*
* If we are recursing, the only case we want to do anything with is a * If we are recursing, the only case we want to do anything with is a
* live leaf page having the current vacuum cycle ID. Any other state * live leaf page having the current vacuum cycle ID. Any other state
* implies we already saw the page (eg, deleted it as being empty). In * implies we already saw the page (eg, deleted it as being empty).
* particular, we don't want to risk adding it to freePages twice.
*/ */
if (blkno != orig_blkno) if (blkno != orig_blkno)
{ {
...@@ -803,12 +771,15 @@ restart: ...@@ -803,12 +771,15 @@ restart:
} }
} }
/* If the page is in use, update lastUsedPage */
if (!_bt_page_recyclable(page) && vstate->lastUsedPage < blkno)
vstate->lastUsedPage = blkno;
/* Page is valid, see what to do with it */ /* Page is valid, see what to do with it */
if (_bt_page_recyclable(page)) if (_bt_page_recyclable(page))
{ {
/* Okay to recycle this page */ /* Okay to recycle this page */
if (vstate->nFreePages < vstate->maxFreePages) RecordFreeIndexPage(rel, blkno);
vstate->freePages[vstate->nFreePages++] = blkno;
vstate->totFreePages++; vstate->totFreePages++;
stats->pages_deleted++; stats->pages_deleted++;
} }
...@@ -944,8 +915,7 @@ restart: ...@@ -944,8 +915,7 @@ restart:
*/ */
if (ndel && info->vacuum_full) if (ndel && info->vacuum_full)
{ {
if (vstate->nFreePages < vstate->maxFreePages) RecordFreeIndexPage(rel, blkno);
vstate->freePages[vstate->nFreePages++] = blkno;
vstate->totFreePages++; vstate->totFreePages++;
} }
......
...@@ -52,12 +52,14 @@ ...@@ -52,12 +52,14 @@
* we log the completed index pages to WAL if and only if WAL archiving is * we log the completed index pages to WAL if and only if WAL archiving is
* active. * active.
* *
* This code isn't concerned about the FSM at all. The caller is responsible
* for initializing that.
* *
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.117 2008/08/11 11:05:10 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.118 2008/09/30 10:52:10 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Resource managers definition * Resource managers definition
* *
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.25 2006/11/05 22:42:07 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.26 2008/09/30 10:52:11 heikki Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/sequence.h" #include "commands/sequence.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "storage/freespace.h"
#include "storage/smgr.h" #include "storage/smgr.h"
...@@ -30,7 +31,7 @@ const RmgrData RmgrTable[RM_MAX_ID + 1] = { ...@@ -30,7 +31,7 @@ const RmgrData RmgrTable[RM_MAX_ID + 1] = {
{"Database", dbase_redo, dbase_desc, NULL, NULL, NULL}, {"Database", dbase_redo, dbase_desc, NULL, NULL, NULL},
{"Tablespace", tblspc_redo, tblspc_desc, NULL, NULL, NULL}, {"Tablespace", tblspc_redo, tblspc_desc, NULL, NULL, NULL},
{"MultiXact", multixact_redo, multixact_desc, NULL, NULL, NULL}, {"MultiXact", multixact_redo, multixact_desc, NULL, NULL, NULL},
{"Reserved 7", NULL, NULL, NULL, NULL, NULL}, {"FreeSpaceMap", fsm_redo, fsm_desc, NULL, NULL, NULL},
{"Reserved 8", NULL, NULL, NULL, NULL, NULL}, {"Reserved 8", NULL, NULL, NULL, NULL, NULL},
{"Heap2", heap2_redo, heap2_desc, NULL, NULL, NULL}, {"Heap2", heap2_redo, heap2_desc, NULL, NULL, NULL},
{"Heap", heap_redo, heap_desc, NULL, NULL, NULL}, {"Heap", heap_redo, heap_desc, NULL, NULL, NULL},
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.58 2008/08/11 11:05:10 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.59 2008/09/30 10:52:11 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -359,6 +359,7 @@ CreateFakeRelcacheEntry(RelFileNode rnode) ...@@ -359,6 +359,7 @@ CreateFakeRelcacheEntry(RelFileNode rnode)
rel->rd_lockInfo.lockRelId.relId = rnode.relNode; rel->rd_lockInfo.lockRelId.relId = rnode.relNode;
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks_cache = InvalidBlockNumber;
rel->rd_smgr = NULL; rel->rd_smgr = NULL;
return rel; return rel;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.245 2008/09/01 20:42:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.246 2008/09/30 10:52:11 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "postmaster/walwriter.h" #include "postmaster/walwriter.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
...@@ -419,7 +418,6 @@ AuxiliaryProcessMain(int argc, char *argv[]) ...@@ -419,7 +418,6 @@ AuxiliaryProcessMain(int argc, char *argv[])
case StartupProcess: case StartupProcess:
bootstrap_signals(); bootstrap_signals();
StartupXLOG(); StartupXLOG();
LoadFreeSpaceMap();
BuildFlatFiles(false); BuildFlatFiles(false);
proc_exit(0); /* startup done */ proc_exit(0); /* startup done */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.339 2008/08/28 23:09:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.340 2008/09/30 10:52:12 heikki Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
...@@ -294,14 +295,22 @@ heap_create(const char *relname, ...@@ -294,14 +295,22 @@ heap_create(const char *relname,
/* /*
* Have the storage manager create the relation's disk file, if needed. * Have the storage manager create the relation's disk file, if needed.
* *
* We only create storage for the main fork here. The caller is * We create storage for the main fork here, and also for the FSM for a
* responsible for creating any additional forks if needed. * heap or toast relation. The caller is responsible for creating any
* additional forks if needed.
*/ */
if (create_storage) if (create_storage)
{ {
Assert(rel->rd_smgr == NULL); Assert(rel->rd_smgr == NULL);
RelationOpenSmgr(rel); RelationOpenSmgr(rel);
smgrcreate(rel->rd_smgr, MAIN_FORKNUM, rel->rd_istemp, false); smgrcreate(rel->rd_smgr, MAIN_FORKNUM, rel->rd_istemp, false);
/*
* For a real heap, create FSM fork as well. Indexams are
* responsible for creating any extra forks themselves.
*/
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE)
smgrcreate(rel->rd_smgr, FSM_FORKNUM, rel->rd_istemp, false);
} }
return rel; return rel;
...@@ -2256,7 +2265,11 @@ RelationTruncateIndexes(Relation heapRelation) ...@@ -2256,7 +2265,11 @@ RelationTruncateIndexes(Relation heapRelation)
/* Fetch info needed for index_build */ /* Fetch info needed for index_build */
indexInfo = BuildIndexInfo(currentIndex); indexInfo = BuildIndexInfo(currentIndex);
/* Now truncate the actual file (and discard buffers) */ /*
* Now truncate the actual file (and discard buffers). The indexam
* is responsible for truncating the FSM in index_build(), if
* applicable.
*/
RelationTruncate(currentIndex, 0); RelationTruncate(currentIndex, 0);
/* Initialize the index and rebuild */ /* Initialize the index and rebuild */
...@@ -2310,7 +2323,8 @@ heap_truncate(List *relids) ...@@ -2310,7 +2323,8 @@ heap_truncate(List *relids)
{ {
Relation rel = lfirst(cell); Relation rel = lfirst(cell);
/* Truncate the actual file (and discard buffers) */ /* Truncate the FSM and actual file (and discard buffers) */
FreeSpaceMapTruncateRel(rel, 0);
RelationTruncate(rel, 0); RelationTruncate(rel, 0);
/* If this relation has indexes, truncate the indexes too */ /* If this relation has indexes, truncate the indexes too */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.304 2008/09/15 18:43:41 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.305 2008/09/30 10:52:12 heikki Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -920,7 +920,7 @@ index_drop(Oid indexId) ...@@ -920,7 +920,7 @@ index_drop(Oid indexId)
RelationOpenSmgr(userIndexRelation); RelationOpenSmgr(userIndexRelation);
for (forknum = 0; forknum <= MAX_FORKNUM; forknum++) for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
if (smgrexists(userIndexRelation->rd_smgr, forknum)) if (smgrexists(userIndexRelation->rd_smgr, forknum))
smgrscheduleunlink(userIndexRelation->rd_smgr, forknum, smgrscheduleunlink(userIndexRelation->rd_smgr, forknum,
userIndexRelation->rd_istemp); userIndexRelation->rd_istemp);
RelationCloseSmgr(userIndexRelation); RelationCloseSmgr(userIndexRelation);
...@@ -1322,7 +1322,7 @@ setNewRelfilenode(Relation relation, TransactionId freezeXid) ...@@ -1322,7 +1322,7 @@ setNewRelfilenode(Relation relation, TransactionId freezeXid)
/* /*
* ... and create storage for corresponding forks in the new relfilenode. * ... and create storage for corresponding forks in the new relfilenode.
* *
* NOTE: any conflict in relfilenode value will be caught here * NOTE: any conflict in relfilenode value will be caught here
*/ */
newrnode = relation->rd_node; newrnode = relation->rd_node;
newrnode.relNode = newrelfilenode; newrnode.relNode = newrelfilenode;
...@@ -1331,6 +1331,14 @@ setNewRelfilenode(Relation relation, TransactionId freezeXid) ...@@ -1331,6 +1331,14 @@ setNewRelfilenode(Relation relation, TransactionId freezeXid)
/* Create the main fork, like heap_create() does */ /* Create the main fork, like heap_create() does */
smgrcreate(srel, MAIN_FORKNUM, relation->rd_istemp, false); smgrcreate(srel, MAIN_FORKNUM, relation->rd_istemp, false);
/*
* For a heap, create FSM fork as well. Indexams are responsible for
* creating any extra forks themselves.
*/
if (relation->rd_rel->relkind == RELKIND_RELATION ||
relation->rd_rel->relkind == RELKIND_TOASTVALUE)
smgrcreate(srel, FSM_FORKNUM, relation->rd_istemp, false);
/* schedule unlinking old files */ /* schedule unlinking old files */
for (i = 0; i <= MAX_FORKNUM; i++) for (i = 0; i <= MAX_FORKNUM; i++)
{ {
...@@ -2310,7 +2318,10 @@ reindex_index(Oid indexId) ...@@ -2310,7 +2318,10 @@ reindex_index(Oid indexId)
if (inplace) if (inplace)
{ {
/* Truncate the actual file (and discard buffers) */ /*
* Truncate the actual file (and discard buffers). The indexam
* is responsible for truncating the FSM, if applicable
*/
RelationTruncate(iRel, 0); RelationTruncate(iRel, 0);
} }
else else
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.212 2008/09/23 10:58:03 heikki Exp $ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.213 2008/09/30 10:52:12 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/smgr.h" #include "storage/smgr.h"
...@@ -796,11 +795,6 @@ dropdb(const char *dbname, bool missing_ok) ...@@ -796,11 +795,6 @@ dropdb(const char *dbname, bool missing_ok)
*/ */
DropDatabaseBuffers(db_id); DropDatabaseBuffers(db_id);
/*
* Also, clean out any entries in the shared free space map.
*/
FreeSpaceMapForgetDatabase(db_id);
/* /*
* Tell the stats collector to forget it immediately, too. * Tell the stats collector to forget it immediately, too.
*/ */
...@@ -1640,9 +1634,6 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -1640,9 +1634,6 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
/* Drop pages for this database that are in the shared buffer cache */ /* Drop pages for this database that are in the shared buffer cache */
DropDatabaseBuffers(xlrec->db_id); DropDatabaseBuffers(xlrec->db_id);
/* Also, clean out any entries in the shared free space map */
FreeSpaceMapForgetDatabase(xlrec->db_id);
/* Also, clean out any fsync requests that might be pending in md.c */ /* Also, clean out any fsync requests that might be pending in md.c */
ForgetDatabaseFsyncRequests(xlrec->db_id); ForgetDatabaseFsyncRequests(xlrec->db_id);
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.377 2008/09/11 14:01:09 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.378 2008/09/30 10:52:12 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -505,14 +505,6 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, ...@@ -505,14 +505,6 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
* (autovacuum.c does this for itself.) * (autovacuum.c does this for itself.)
*/ */
vac_update_datfrozenxid(); vac_update_datfrozenxid();
/*
* If it was a database-wide VACUUM, print FSM usage statistics (we
* don't make you be superuser to see these). We suppress this in
* autovacuum, too.
*/
if (all_rels)
PrintFreeSpaceMapStatistics(elevel);
} }
/* /*
...@@ -1272,8 +1264,9 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) ...@@ -1272,8 +1264,9 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
} }
} }
/* update shared free space map with final free space info */ /* update thefree space map with final free space info, and vacuum it */
vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages); vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);
FreeSpaceMapVacuum(onerel);
/* update statistics in pg_class */ /* update statistics in pg_class */
vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages, vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
...@@ -2849,6 +2842,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -2849,6 +2842,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
/* Truncate relation, if needed */ /* Truncate relation, if needed */
if (blkno < nblocks) if (blkno < nblocks)
{ {
FreeSpaceMapTruncateRel(onerel, blkno);
RelationTruncate(onerel, blkno); RelationTruncate(onerel, blkno);
vacrelstats->rel_pages = blkno; /* set new number of blocks */ vacrelstats->rel_pages = blkno; /* set new number of blocks */
} }
...@@ -3243,6 +3237,7 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages) ...@@ -3243,6 +3237,7 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
(errmsg("\"%s\": truncated %u to %u pages", (errmsg("\"%s\": truncated %u to %u pages",
RelationGetRelationName(onerel), RelationGetRelationName(onerel),
vacrelstats->rel_pages, relblocks))); vacrelstats->rel_pages, relblocks)));
FreeSpaceMapTruncateRel(onerel, relblocks);
RelationTruncate(onerel, relblocks); RelationTruncate(onerel, relblocks);
vacrelstats->rel_pages = relblocks; /* set new number of blocks */ vacrelstats->rel_pages = relblocks; /* set new number of blocks */
} }
...@@ -3475,8 +3470,8 @@ tid_reaped(ItemPointer itemptr, void *state) ...@@ -3475,8 +3470,8 @@ tid_reaped(ItemPointer itemptr, void *state)
} }
/* /*
* Update the shared Free Space Map with the info we now have about * Update the Free Space Map with the info we now have about free space in
* free space in the relation, discarding any old info the map may have. * the relation.
*/ */
static void static void
vac_update_fsm(Relation onerel, VacPageList fraged_pages, vac_update_fsm(Relation onerel, VacPageList fraged_pages,
...@@ -3484,26 +3479,8 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages, ...@@ -3484,26 +3479,8 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages,
{ {
int nPages = fraged_pages->num_pages; int nPages = fraged_pages->num_pages;
VacPage *pagedesc = fraged_pages->pagedesc; VacPage *pagedesc = fraged_pages->pagedesc;
Size threshold;
FSMPageData *pageSpaces;
int outPages;
int i; int i;
/*
* We only report pages with free space at least equal to the average
* request size --- this avoids cluttering FSM with uselessly-small bits
* of space. Although FSM would discard pages with little free space
* anyway, it's important to do this prefiltering because (a) it reduces
* the time spent holding the FSM lock in RecordRelationFreeSpace, and (b)
* FSM uses the number of pages reported as a statistic for guiding space
* management. If we didn't threshold our reports the same way
* vacuumlazy.c does, we'd be skewing that statistic.
*/
threshold = GetAvgFSMRequestSize(&onerel->rd_node);
pageSpaces = (FSMPageData *) palloc(nPages * sizeof(FSMPageData));
outPages = 0;
for (i = 0; i < nPages; i++) for (i = 0; i < nPages; i++)
{ {
/* /*
...@@ -3514,17 +3491,9 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages, ...@@ -3514,17 +3491,9 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages,
if (pagedesc[i]->blkno >= rel_pages) if (pagedesc[i]->blkno >= rel_pages)
break; break;
if (pagedesc[i]->free >= threshold) RecordPageWithFreeSpace(onerel, pagedesc[i]->blkno, pagedesc[i]->free);
{
FSMPageSetPageNum(&pageSpaces[outPages], pagedesc[i]->blkno);
FSMPageSetSpace(&pageSpaces[outPages], pagedesc[i]->free);
outPages++;
}
} }
RecordRelationFreeSpace(&onerel->rd_node, outPages, outPages, pageSpaces);
pfree(pageSpaces);
} }
/* Copy a VacPage structure */ /* Copy a VacPage structure */
......
This diff is collapsed.
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.51 2008/08/11 11:05:11 heikki Exp $ * $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.52 2008/09/30 10:52:13 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -55,7 +55,6 @@ ...@@ -55,7 +55,6 @@
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
...@@ -398,7 +397,6 @@ BackgroundWriterMain(void) ...@@ -398,7 +397,6 @@ BackgroundWriterMain(void)
ExitOnAnyError = true; ExitOnAnyError = true;
/* Close down the database */ /* Close down the database */
ShutdownXLOG(0, 0); ShutdownXLOG(0, 0);
DumpFreeSpaceMap(0, 0);
/* Normal exit from the bgwriter is here */ /* Normal exit from the bgwriter is here */
proc_exit(0); /* done */ proc_exit(0); /* done */
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for storage/freespace # Makefile for storage/freespace
# #
# IDENTIFICATION # IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/storage/freespace/Makefile,v 1.4 2008/02/19 10:30:08 petere Exp $ # $PostgreSQL: pgsql/src/backend/storage/freespace/Makefile,v 1.5 2008/09/30 10:52:13 heikki Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,6 +12,6 @@ subdir = src/backend/storage/freespace ...@@ -12,6 +12,6 @@ subdir = src/backend/storage/freespace
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = freespace.o OBJS = freespace.o fsmpage.o indexfsm.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
$PostgreSQL: pgsql/src/backend/storage/freespace/README,v 1.1 2008/09/30 10:52:13 heikki Exp $
Free Space Map
--------------
The purpose of the free space map is to quickly locate a page with enough
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.
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.
We allocate one map byte to each page, allowing us to record free space
at a granularity of 1/256th of a page. Another way to say it is that
the stored value is the free space divided by BLCKSZ/256 (rounding down).
We assume that the free space must always be less than BLCKSZ, since
all pages have some overhead; so the maximum map value is 255.
To assist in fast searching, the map isn't simply an array of per-page
entries, but has a tree structure above those entries. There is a tree
structure of pages, and a tree structure within each page, as described
below.
FSM page structure
------------------
Within each FSM page, we use a binary tree structure where leaf nodes store
the amount of free space on heap pages (or lower level FSM pages, see
"Higher-level structure" below), with one leaf node per heap page. A non-leaf
node stores the max amount of free space on any of its children.
For example:
4
4 2
3 4 0 2 <- This level represents heap pages
We need two basic operations: search and update.
To search for a page with X amount of free space, traverse down the tree
along a path where n >= X, until you hit the bottom. If both children of a
node satisfy the condition, you can pick either one arbitrarily.
To update the amount of free space on a page to X, first update the leaf node
corresponding to the heap page, then "bubble up" the change to upper nodes,
by walking up to each parent and recomputing its value as the max of its
two children. Repeat until reaching the root or a parent whose value
doesn't change.
This data structure has a couple of nice properties:
- to discover that there is no page with X bytes of free space, you only
need to look at the root node
- by varying which child to traverse to in the search algorithm, when you have
a choice, we can implement various strategies, like preferring pages closer
to a given page, or spreading the load across the table.
Higher-level routines that use FSM pages access them through the fsm_set_avail()
and fsm_search_avail() functions. The interface to those functions hides the
page's internal tree structure, treating the FSM page as a black box that has
a certain number of "slots" for storing free space information. (However,
the higher routines have to be aware of the tree structure of the whole map.)
The binary tree is stored on each FSM page as an array. Because the page
header takes some space on a page, the binary tree isn't perfect. That is,
a few right-most leaf nodes are missing, and there are some useless non-leaf
nodes at the right. So the tree looks something like this:
0
1 2
3 4 5 6
7 8 9 A B
where the numbers denote each node's position in the array. Note that the
tree is guaranteed complete above the leaf level; only some leaf nodes are
missing. This is reflected in the number of usable "slots" per page not
being an exact power of 2.
A FSM page also has a next slot pointer, fp_next_slot, that determines where
to start the next search for free space within that page. The reason for that
is to spread out the pages that are returned by FSM searches. When several
backends are concurrently inserting into a relation, contention can be avoided
by having them insert into different pages. But it is also desirable to fill
up pages in sequential order, to get the benefit of OS prefetching and batched
writes. The FSM is responsible for making that happen, and the next slot
pointer helps provide the desired behavior.
Higher-level structure
----------------------
To scale up the data structure described above beyond a single page, we
maintain a similar tree-structure across pages. Leaf nodes in higher level
pages correspond to lower level FSM pages. The root node within each page
has the same value as the corresponding leaf node on its parent page.
The root page is always stored at physical block 0.
For example, assuming each FSM page can hold information about 4 pages (in
reality, it holds (BLCKSZ - headers) / 2, or ~4000 with default BLCKSZ),
we get a disk layout like this:
0 <-- page 0 at level 2 (root page)
0 <-- page 0 at level 1
0 <-- page 0 at level 0
1 <-- page 1 at level 0
2 <-- ...
3
1 <-- page 1 at level 1
4
5
6
7
2
8
9
10
11
3
12
13
14
15
where the numbers are page numbers *at that level*, starting from 0.
To find the physical block # corresponding to leaf page n, we need to
count the number number of leaf and upper-level pages preceding page n.
This turns out to be
y = n + (n / F + 1) + (n / F^2 + 1) + ... + 1
where F is the fanout (4 in the above example). The first term n is the number
of preceding leaf pages, the second term is the number of pages at level 1,
and so forth.
To keep things simple, the tree is always constant height. To cover the
maximum relation size of 2^32-1 blocks, three levels is enough with the default
BLCKSZ (4000^3 > 2^32).
Addressing
----------
The higher-level routines operate on "logical" addresses, consisting of
- level,
- logical page number, and
- slot (if applicable)
Bottom level FSM pages have level of 0, the level above that 1, and root 2.
As in the diagram above, logical page number is the page number at that level,
starting from 0.
Locking
-------
When traversing down to search for free space, only one page is locked at a
time: the parent page is released before locking the child. If the child page
is concurrently modified, and there no longer is free space on the child page
when you land on it, you need to start from scratch (after correcting the
parent page, so that you don't get into an infinite loop).
We use shared buffer locks when searching, but exclusive buffer lock when
updating a page. However, the next slot search pointer is updated during
searches even though we have only a shared lock. fp_next_slot is just a hint
and we can easily reset it if it gets corrupted; so it seems better to accept
some risk of that type than to pay the overhead of exclusive locking.
Recovery
--------
The FSM is not explicitly WAL-logged. Instead, we rely on a bunch of
self-correcting measures to repair possible corruption.
First of all, whenever a value is set on an FSM page, the root node of the
page is compared against the new value after bubbling up the change is
finished. It should be greater than or equal to the value just set, or we
have a corrupted page, with a parent somewhere with too small a value.
Secondly, if we detect corrupted pages while we search, traversing down
the tree. That check will notice if a parent node is set to too high a value.
In both cases, the upper nodes on the page are immediately rebuilt, fixing
the corruption.
Vacuum updates all the bottom level pages with correct amount of free space
on the heap pages, fixing any outdated values there. After the heap and
index passes are done, FreeSpaceMapVacuum is called, and the FSM tree is
scanned in depth-first order. This fixes any discrepancies between upper
and lower level FSM pages.
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.
This diff is collapsed.
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* indexfsm.c
* POSTGRES free space map for quickly finding free pages in relations
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/freespace/indexfsm.c,v 1.1 2008/09/30 10:52:13 heikki Exp $
*
*
* NOTES:
*
* This is similar to the FSM used for heap, in freespace.c, but instead
* of tracking the amount of free space on pages, we only track whether
* pages are completely free or in-use. We use the same FSM implementation
* as for heaps, using BLCKSZ - 1 to denote used pages, and 0 for unused.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/smgr.h"
/*
* Exported routines
*/
/*
* InitIndexFreeSpaceMap - Create or reset the FSM fork for relation.
*/
void
InitIndexFreeSpaceMap(Relation rel)
{
/* Create FSM fork if it doesn't exist yet, or truncate it if it does */
RelationOpenSmgr(rel);
if (!smgrexists(rel->rd_smgr, FSM_FORKNUM))
smgrcreate(rel->rd_smgr, FSM_FORKNUM, rel->rd_istemp, false);
else
smgrtruncate(rel->rd_smgr, FSM_FORKNUM, 0, rel->rd_istemp);
}
/*
* GetFreeIndexPage - return a free page from the FSM
*
* As a side effect, the page is marked as used in the FSM.
*/
BlockNumber
GetFreeIndexPage(Relation rel)
{
BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ/2);
if (blkno != InvalidBlockNumber)
RecordUsedIndexPage(rel, blkno);
return blkno;
}
/*
* RecordFreeIndexPage - mark a page as free in the FSM
*/
void
RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
{
RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1);
}
/*
* RecordUsedIndexPage - mark a page as used in the FSM
*/
void
RecordUsedIndexPage(Relation rel, BlockNumber usedBlock)
{
RecordPageWithFreeSpace(rel, usedBlock, 0);
}
/*
* IndexFreeSpaceMapTruncate - adjust for truncation of a relation.
*
* We need to delete any stored data past the new relation length, so that
* we don't bogusly return removed block numbers.
*/
void
IndexFreeSpaceMapTruncate(Relation rel, BlockNumber nblocks)
{
FreeSpaceMapTruncateRel(rel, nblocks);
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.96 2008/05/12 00:00:50 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.97 2008/09/30 10:52:13 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h" #include "postmaster/postmaster.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/pg_shmem.h" #include "storage/pg_shmem.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
...@@ -110,7 +109,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) ...@@ -110,7 +109,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
size = add_size(size, ProcArrayShmemSize()); size = add_size(size, ProcArrayShmemSize());
size = add_size(size, BackendStatusShmemSize()); size = add_size(size, BackendStatusShmemSize());
size = add_size(size, SInvalShmemSize()); size = add_size(size, SInvalShmemSize());
size = add_size(size, FreeSpaceShmemSize());
size = add_size(size, BgWriterShmemSize()); size = add_size(size, BgWriterShmemSize());
size = add_size(size, AutoVacuumShmemSize()); size = add_size(size, AutoVacuumShmemSize());
size = add_size(size, BTreeShmemSize()); size = add_size(size, BTreeShmemSize());
...@@ -203,11 +201,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) ...@@ -203,11 +201,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
*/ */
CreateSharedInvalidationState(); CreateSharedInvalidationState();
/*
* Set up free-space map
*/
InitFreeSpaceMap();
/* /*
* Set up interprocess signaling mechanisms * Set up interprocess signaling mechanisms
*/ */
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.111 2008/08/11 11:05:11 heikki Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.112 2008/09/30 10:52:13 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
...@@ -474,13 +473,6 @@ smgr_internal_unlink(RelFileNode rnode, ForkNumber forknum, ...@@ -474,13 +473,6 @@ smgr_internal_unlink(RelFileNode rnode, ForkNumber forknum,
*/ */
DropRelFileNodeBuffers(rnode, forknum, isTemp, 0); DropRelFileNodeBuffers(rnode, forknum, isTemp, 0);
/*
* Tell the free space map to forget this relation. It won't be accessed
* any more anyway, but we may as well recycle the map space quickly.
*/
if (forknum == MAIN_FORKNUM)
FreeSpaceMapForgetRel(&rnode);
/* /*
* It'd be nice to tell the stats collector to forget it immediately, too. * It'd be nice to tell the stats collector to forget it immediately, too.
* But we can't because we don't know the OID (and in cases involving * But we can't because we don't know the OID (and in cases involving
...@@ -577,13 +569,6 @@ smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks, ...@@ -577,13 +569,6 @@ smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks,
*/ */
DropRelFileNodeBuffers(reln->smgr_rnode, forknum, isTemp, nblocks); DropRelFileNodeBuffers(reln->smgr_rnode, forknum, isTemp, nblocks);
/*
* Tell the free space map to forget anything it may have stored for the
* about-to-be-deleted blocks. We want to be sure it won't return bogus
* block numbers later on.
*/
FreeSpaceMapTruncateRel(&reln->smgr_rnode, nblocks);
/* Do the truncation */ /* Do the truncation */
(*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, forknum, nblocks, (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, forknum, nblocks,
isTemp); isTemp);
...@@ -905,13 +890,6 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -905,13 +890,6 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
DropRelFileNodeBuffers(xlrec->rnode, xlrec->forknum, false, DropRelFileNodeBuffers(xlrec->rnode, xlrec->forknum, false,
xlrec->blkno); xlrec->blkno);
/*
* Tell the free space map to forget anything it may have stored for
* the about-to-be-deleted blocks. We want to be sure it won't return
* bogus block numbers later on.
*/
FreeSpaceMapTruncateRel(&reln->smgr_rnode, xlrec->blkno);
/* Do the truncation */ /* Do the truncation */
(*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln,
xlrec->forknum, xlrec->forknum,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.556 2008/08/19 18:30:04 tgl Exp $ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.557 2008/09/30 10:52:13 heikki Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -57,7 +57,6 @@ ...@@ -57,7 +57,6 @@
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
#include "rewrite/rewriteHandler.h" #include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/sinval.h" #include "storage/sinval.h"
...@@ -3258,13 +3257,6 @@ PostgresMain(int argc, char *argv[], const char *username) ...@@ -3258,13 +3257,6 @@ PostgresMain(int argc, char *argv[], const char *username)
StartupXLOG(); StartupXLOG();
on_shmem_exit(ShutdownXLOG, 0); on_shmem_exit(ShutdownXLOG, 0);
/*
* Read any existing FSM cache file, and register to write one out at
* exit.
*/
LoadFreeSpaceMap();
on_shmem_exit(DumpFreeSpaceMap, 0);
/* /*
* We have to build the flat file for pg_database, but not for the * We have to build the flat file for pg_database, but not for the
* user and group tables, since we won't try to do authentication. * user and group tables, since we won't try to do authentication.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.273 2008/08/10 19:02:33 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.274 2008/09/30 10:52:13 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -304,6 +304,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) ...@@ -304,6 +304,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
*/ */
MemSet(relation, 0, sizeof(RelationData)); MemSet(relation, 0, sizeof(RelationData));
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL; relation->rd_smgr = NULL;
...@@ -1364,6 +1365,7 @@ formrdesc(const char *relationName, Oid relationReltype, ...@@ -1364,6 +1365,7 @@ formrdesc(const char *relationName, Oid relationReltype,
*/ */
relation = (Relation) palloc0(sizeof(RelationData)); relation = (Relation) palloc0(sizeof(RelationData));
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL; relation->rd_smgr = NULL;
...@@ -1652,8 +1654,9 @@ RelationReloadIndexInfo(Relation relation) ...@@ -1652,8 +1654,9 @@ RelationReloadIndexInfo(Relation relation)
heap_freetuple(pg_class_tuple); heap_freetuple(pg_class_tuple);
/* We must recalculate physical address in case it changed */ /* We must recalculate physical address in case it changed */
RelationInitPhysicalAddr(relation); RelationInitPhysicalAddr(relation);
/* Make sure targblock is reset in case rel was truncated */ /* Must reset targblock and fsm_nblocks_cache in case rel was truncated */
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* Must free any AM cached data, too */ /* Must free any AM cached data, too */
if (relation->rd_amcache) if (relation->rd_amcache)
pfree(relation->rd_amcache); pfree(relation->rd_amcache);
...@@ -1736,6 +1739,7 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1736,6 +1739,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (relation->rd_isnailed) if (relation->rd_isnailed)
{ {
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks_cache = InvalidBlockNumber;
if (relation->rd_rel->relkind == RELKIND_INDEX) if (relation->rd_rel->relkind == RELKIND_INDEX)
{ {
relation->rd_isvalid = false; /* needs to be revalidated */ relation->rd_isvalid = false; /* needs to be revalidated */
...@@ -2330,6 +2334,7 @@ RelationBuildLocalRelation(const char *relname, ...@@ -2330,6 +2334,7 @@ RelationBuildLocalRelation(const char *relname,
rel = (Relation) palloc0(sizeof(RelationData)); rel = (Relation) palloc0(sizeof(RelationData));
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks_cache = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
rel->rd_smgr = NULL; rel->rd_smgr = NULL;
...@@ -3586,6 +3591,7 @@ load_relcache_init_file(void) ...@@ -3586,6 +3591,7 @@ load_relcache_init_file(void)
*/ */
rel->rd_smgr = NULL; rel->rd_smgr = NULL;
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks_cache = InvalidBlockNumber;
if (rel->rd_isnailed) if (rel->rd_isnailed)
rel->rd_refcnt = 1; rel->rd_refcnt = 1;
else else
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.473 2008/09/23 21:12:03 mha Exp $ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.474 2008/09/30 10:52:13 heikki Exp $
* *
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
...@@ -57,7 +57,6 @@ ...@@ -57,7 +57,6 @@
#include "regex/regex.h" #include "regex/regex.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/freespace.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h" #include "tsearch/ts_cache.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -446,8 +445,6 @@ const char *const config_group_names[] = ...@@ -446,8 +445,6 @@ const char *const config_group_names[] =
gettext_noop("Resource Usage"), gettext_noop("Resource Usage"),
/* RESOURCES_MEM */ /* RESOURCES_MEM */
gettext_noop("Resource Usage / Memory"), gettext_noop("Resource Usage / Memory"),
/* RESOURCES_FSM */
gettext_noop("Resource Usage / Free Space Map"),
/* RESOURCES_KERNEL */ /* RESOURCES_KERNEL */
gettext_noop("Resource Usage / Kernel Resources"), gettext_noop("Resource Usage / Kernel Resources"),
/* WAL */ /* WAL */
...@@ -1528,23 +1525,6 @@ static struct config_int ConfigureNamesInt[] = ...@@ -1528,23 +1525,6 @@ static struct config_int ConfigureNamesInt[] =
100000000, 0, 1000000000, NULL, NULL 100000000, 0, 1000000000, NULL, NULL
}, },
{
{"max_fsm_relations", PGC_POSTMASTER, RESOURCES_FSM,
gettext_noop("Sets the maximum number of tables and indexes for which free space is tracked."),
NULL
},
&MaxFSMRelations,
1000, 100, INT_MAX, NULL, NULL
},
{
{"max_fsm_pages", PGC_POSTMASTER, RESOURCES_FSM,
gettext_noop("Sets the maximum number of disk pages for which free space is tracked."),
NULL
},
&MaxFSMPages,
20000, 1000, INT_MAX, NULL, NULL
},
{ {
{"max_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT, {"max_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT,
gettext_noop("Sets the maximum number of locks per transaction."), gettext_noop("Sets the maximum number of locks per transaction."),
......
...@@ -114,13 +114,6 @@ ...@@ -114,13 +114,6 @@
#maintenance_work_mem = 16MB # min 1MB #maintenance_work_mem = 16MB # min 1MB
#max_stack_depth = 2MB # min 100kB #max_stack_depth = 2MB # min 100kB
# - Free Space Map -
#max_fsm_pages = 204800 # min max_fsm_relations*16, 6 bytes each
# (change requires restart)
#max_fsm_relations = 1000 # min 100, ~70 bytes each
# (change requires restart)
# - Kernel Resource Usage - # - Kernel Resource Usage -
#max_files_per_process = 1000 # min 25 #max_files_per_process = 1000 # min 25
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment