Setting up logging for a PostgreSQL server using syslog on a Linux machine is intuitive especially with logging systems like syslog-ng, you just need to put the correct parameters at the right place.

First, you need to setup the system side, by adding the following settings in /etc/syslog-nd/syslog-nd.conf (or similar, don’t hesitate to customize that with your own paths).
destination postgres { file("/var/log/pgsql"); };
filter f_postgres { facility(local0); };
log { source(src); filter(f_postgres); destination(postgres); };

This will send all the logs of postgresql server to /var/log/pgsql. Be sure to combine that with some solution rotating log files to avoid a single file becoming too large… And reload syslog-ng with a command similar to that (varies depending on distribution used, here Archlinux).
systemctl reload syslog-ng

Then, you need to add those settings in postgresql.conf.
log_destination = 'syslog' # Can specify multiple destinations
syslog_facility='LOCAL0'
syslog_ident='postgres'

Based on the documentation, syslog_facility can be set from LOCAL0 to LOCAL7.
Don’t forget that you can also specify multiple log destinations. For example when using stderr and syslog at the same time, simply do that:
log_destination = 'stderr,syslog'

Finally, reload the parameters of server and you are done.
pg_ctl reload -D $PGDATA
Note that restarting the server is not necessary.

PostgreSQL 9.3 comes with a pretty cool feature called materialized views. It has been created by Kevin Grittner and committed by the same person not so long ago.
commit 3bf3ab8c563699138be02f9dc305b7b77a724307
Author: Kevin Grittner
Date: Sun Mar 3 18:23:31 2013 -0600
 
Add a materialized view relations.
 
A materialized view has a rule just like a view and a heap and
other physical properties like a table. The rule is only used to
populate the table, references in queries refer to the
materialized data.
 
This is a minimal implementation, but should still be useful in
many cases. Currently data is only populated "on demand" by the
CREATE MATERIALIZED VIEW and REFRESH MATERIALIZED VIEW statements.
It is expected that future releases will add incremental updates
with various timings, and that a more refined concept of defining
what is "fresh" data will be developed. At some point it may even
be possible to have queries use a materialized in place of
references to underlying tables, but that requires the other
above-mentioned features to be working first.
 
Much of the documentation work by Robert Haas.
Review by Noah Misch, Thom Brown, Robert Haas, Marko Tiikkaja
Security review by KaiGai Kohei, with a decision on how best to
implement sepgsql still pending.

What is a materialized view? In short, it is the mutant of a table and a view. A view is a projection of data in a given relation and has no storage. A table is well… A table…
Between that, a materialized view is a projection of table data and has its own storage. It uses a query to fetch its data like a view, but this data is stored like a common table. The materialized view can also be refreshed with updated data by running once again the query it uses for its projection, or have its data truncated. In the last case it is left in an non-scannable state. Also, as a materialized view has its proper storage, it can use tablespaces and its own indexes. Note that it can also be an unlogged relation.

This feature introduces four new SQL commands:

CREATE, ALTER and DROP are common DDL commands here to manipulate the definition of materialized views. What is important here is the new command REFRESH (its name has been a long debate inside the community). This command can be used to update the materialized view with fresh data by running once again the scanning query. Note that REFRESH can also be used to *truncate* (not really though) the data of the relation by running it with the clause WITH NO DATA.

Materialized views have their own advantages in many scenarios: faster access to data than needs to be brought from a remote server (read a file on postgres server through file_fdw, etc.), using data that needs to be refreshed periodically (cache system), projecting data with embedded ORDER BY from a large table, running an expensive join in background periodically, etc.

I can also imagine some nice combinations with data refresh and custom background workers. Who said that automatic data refresh on a materialized view was not possible?

Now let’s have a look at how it works.
postgres=# CREATE TABLE aa AS SELECT generate_series(1,1000000) AS a;
SELECT 1000000
postgres=# CREATE VIEW aav AS SELECT * FROM aa WHERE a <= 500000;
CREATE VIEW
postgres=# CREATE MATERIALIZED VIEW aam AS SELECT * FROM aa WHERE a <= 500000;
SELECT 500000

Here is the size that each relation uses.
postgres=# SELECT pg_relation_size('aa') AS tab_size, pg_relation_size('aav') AS view_size, pg_relation_size('aam') AS matview_size;
tab_size | view_size | matview_size
----------+-----------+--------------
36249600 | 0 | 18137088
(1 row)

A materialized view uses storage (here 18M), as much as it needs to store the data it fetched from its parent table (with size of 36M) when running the view query.

The refresh of a materialized view can be controlled really easily.
postgres=# DELETE FROM aa WHERE a <= 500000;
DELETE 500000
postgres=# SELECT count(*) FROM aam;
count
--------
500000
(1 row)
postgres=# REFRESH MATERIALIZED VIEW aam;
REFRESH MATERIALIZED VIEW
postgres=# SELECT count(*) FROM aam;
count
-------
0
(1 row)

The new status of table aa is effective on its materialized view aam only once REFRESH has been kicked. Note that at the time of this post, REFRESH uses an exclusive lock (ugh...).

A materialized view can also be set as not scannable thanks to the clause WITH NO DATA of REFRESH.
postgres=# REFRESH MATERIALIZED VIEW aam WITH NO DATA;
REFRESH MATERIALIZED VIEW
postgres=# SELECT count(*) FROM aam;
ERROR: materialized view "aam" has not been populated
HINT: Use the REFRESH MATERIALIZED VIEW command.

There is a new catalog table to help you find the current state of materialized views called pg_matviews.
postgres=# SELECT matviewname, isscannable FROM pg_matviews;
matviewname | isscannable
-------------+-------------
aam | f
(1 row)

It is also not possible to run DML queries on it. This makes sense as the data this view has might not reflect the current state of its parent relation(s). On the contrary, a simple view runs its underlying query each time it is needed, so a parent table could be modified through it (per se updatable views).
postgres=# INSERT INTO aam VALUES (1);
ERROR: cannot change materialized view "aam"
postgres=# UPDATE aam SET a = 5;
ERROR: cannot change materialized view "aam"
postgres=# DELETE FROM aam;
ERROR: cannot change materialized view "aam"

Now, a couple of words about performance improvement and degradation you can have with materialized views as you can manipulate indexes on those relations. For example, it is easily possible to improve queries on the materialized views without caring about the schema of its parent relations.
postgres=# EXPLAIN ANALYZE SELECT * FROM aam WHERE a = 1;
QUERY PLAN
--------------------------------------------------------------------------------------------------
Seq Scan on aam (cost=0.00..8464.00 rows=1 width=4) (actual time=0.060..155.934 rows=1 loops=1)
Filter: (a = 1)
Rows Removed by Filter: 499999
Total runtime: 156.047 ms
(4 rows)
postgres=# CREATE INDEX aam_ind ON aam (a);
CREATE INDEX
postgres=# EXPLAIN ANALYZE SELECT * FROM aam WHERE a = 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Index Only Scan using aam_ind on aam (cost=0.42..8.44 rows=1 width=4) (actual time=2.096..2.101 rows=1 loops=1)
Index Cond: (a = 1)
Heap Fetches: 1
Total runtime: 2.196 ms
(4 rows)

Take care also that indexes and constraint (materialized views can have constraints!) of the parent relation are not copied with the materialized view. For example, a fast query scanning some table's primary key might finish with a deadly sequential scan if it is run on an underlying materialized view based on this table.
postgres=# INSERT INTO bb VALUES (generate_series(1,100000));
INSERT 0 100000
postgres=# EXPLAIN ANALYZE SELECT * FROM bb WHERE a = 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Index Only Scan using bb_pkey on bb (cost=0.29..8.31 rows=1 width=4) (actual time=0.078..0.080 rows=1 loops=1)
Index Cond: (a = 1)
Heap Fetches: 1
Total runtime: 0.159 ms
(4 rows)
postgres=# CREATE MATERIALIZED VIEW bbm AS SELECT * FROM bb;
SELECT 100000
postgres=# EXPLAIN ANALYZE SELECT * FROM bbm WHERE a = 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Seq Scan on bbm (cost=0.00..1776.00 rows=533 width=4) (actual time=0.144..41.873 rows=1 loops=1)
Filter: (a = 1)
Rows Removed by Filter: 99999
Total runtime: 41.935 ms
(4 rows)

Such designs are of course not recommended on a production system, only be aware that bad designs will badly impact your application performance (that's always the case btw).

It is really a nice thing to have particularly for caching applications! So enjoy!

pg_xlogdump is a new contrib module introduced in PostgreSQL 9.3 by this commit.
commit 639ed4e84b7493594860f56b78b25fd113e78fd7
Author: Alvaro Herrera
Date: Fri Feb 22 16:46:24 2013 -0300
 
Add pg_xlogdump contrib program
 
This program relies on rm_desc backend routines and the xlogreader
infrastructure to emit human-readable rendering of WAL records.
 
Author: Andres Freund, with many reworks by Alvaro
Reviewed (in a much earlier version) by Peter Eisentraut

Mainly useful for educational and debugging purposes, pg_xlogdump can be used to understand the internals of PostgreSQL by dumping the WAL (Write-ahead log, which is the basic mechanism used by the server for transaction replay during recovery) into a shape humanly readable.

Just a little bit more information about WAL… Its information is stored in files located in pg_xlog of $PGDATA whose name respect a format name subdivided into 3 sequences of 8 hexa digits defining:

  • Timeline ID
  • Block ID
  • Segment ID

The counter for blocks is incremented once segments are filled.

Postgres includes a couple of functions that can help you to determine in which file is located a given WAL record (pg_xlogfile_name) or what is the current WAL position (pg_current_xlog_location).
michael=# select pg_current_xlog_location();
pg_current_xlog_location
--------------------------
4A/1799988
(1 row)
michael=# select pg_xlogfile_name('4A/1799988');
pg_xlogfile_name
--------------------------
000000010000004A00000001
(1 row)

Here the server is currently on timeline 1, with a Block ID of 4A and a segment ID of 1. The composition of Block ID + segment ID is a LSN or log sequence number, for example ’4A/1799988′. The XLOG files are located in the folder pg_xlog of $PGDATA. Each file has a size of 16MB, and server switches to a new file once this maximum size is reached or once no new file has been written since a time of archive_timeout, parameter of postgresql.conf.

After this digression, let’s have a look at what this utility can do.
The only mandatory option is to specify a start from where the dump will be taken, by either specifying a start LSN with –start or a start WAL file with commands similar to that.
pg_xlogdump --start 0/010EA4D0
pg_xlogdump 000000010000000000000001

If no path is specified to scan the segment files in a given directory, the default is to look if there is a folder called pg_xlog in current directory and get results from it.
In a dump you will get information like that for each WAL record:
rmgr: Heap len (rec/tot): 143/ 175, tx: 688, lsn: 0/02010BE8, prev 0/02010BA0, bkp: 0000, desc: insert: rel 1663/16384/11782; tid 41/54
Each field being in more details:

  • rmgr, the resource manager involved, can be filtered with option -r/–rmgr
  • len, about the length of the record
  • tx, ID of transaction involved with this record, can be filtered with option -x/–xid
  • lsn, log sequence number, including the previous and current lsn, can be filtered with –start and –end.
  • bkp, for the backup block
  • desc, for the description of the action record is doing, and some information related to the relation and page item with which interacts the action

WAL records are divided by resource managers, like database, tablespace, sequence, heap, etc. Based on that you can filter the dump depending on resource manager you want to see. To get a complete list of the resource managers available, simply do that:
pg_xlogdump --rmgr=list

For example, let’s create a sequence on server.
postgres=# create sequence aas;
CREATE SEQUENCE

Here is what you dump when filtering for the resource manager Sequence.
$ pg_xlogdump --start 0/02000000 -r Sequence
rmgr: Sequence len (rec/tot): 158/ 190, tx: 688, lsn: 0/02013C18, prev 0/02013B60, bkp: 0000, desc: log: rel 1663/16384/16390

You can then for example dump all the WAL records for this transaction by running a command like that:
pg_xlogdump --start 0/02000000 -x 688

Only a short introduction of what you can do this module is provided in this post, so for fore details feel free to have a look at the documentation about the available options and what you can do (and cannot do) with pg_xlogdump. Honestly I think it is a good tool to understand the internals of Postgres for newcomers.

Up to Postgres 9.2, the only foreign data wrapper present in core was file_fdw, allowing you to query files as remote tables. This has been corrected with the addition of a second foreign data wrapper called postgres_fdw. This one simply allows to query foreign Postgres servers and fetch results directly on your local server. It has been introduced by this commit.
commit d0d75c402217421b691050857eb3d7af82d0c770
Author: Tom Lane
Date: Thu Feb 21 05:26:23 2013 -0500
 
Add postgres_fdw contrib module.
 
There's still a lot of room for improvement, but it basically works,
and we need this to be present before we can do anything much with the
writable-foreign-tables patch. So let's commit it and get on with testing.
 
Shigeru Hanada, reviewed by KaiGai Kohei and Tom Lane

Documentation can be found here for the time being.

In order to install it from source, do the following commands from the Postgres root folder.
cd contrib/postgres_fdw
make install

Then connect to your existing Postgres server and finish the installation with CREATE EXTENSION.
postgres=# CREATE EXTENSION postgres_fdw;
CREATE EXTENSION
postgres=# \dx postgres_fdw
List of installed extensions
Name | Version | Schema | Description
--------------+---------+--------+----------------------------------------------------
postgres_fdw | 1.0 | public | foreign-data wrapper for remote PostgreSQL servers
(1 row)

Now let’s test it with the case of a simple cluster with one slave running with port 5532 on the same server as its master. Here is the configuration.
$ psql -p 5532 -c 'select pg_is_in_recovery()'
pg_is_in_recovery
-------------------
t
(1 row)

When using a foreign data wrapper, you need to create first a server.
postgres=# CREATE SERVER postgres_server
postgres=# FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'localhost', port '5532', dbname 'postgres');
CREATE SERVER
postgres=# \des
List of foreign servers
Name | Owner | Foreign-data wrapper
-----------------+--------+----------------------
postgres_server | xxxxxx | postgres_fdw
(1 row)

Then let’s move on with a user mapping and a table to query.
postgres=# CREATE USER MAPPING FOR PUBLIC SERVER postgres_server OPTIONS (password '');
CREATE USER MAPPING
postgres=# CREATE TABLE aa AS SELECT 1 AS a, generate_series(1,3) AS b;
CREATE TABLE

As the foreign server used is the slave of our master, there is no need to create this table on the second node.

What remains is the creation of the foreign table.
postgres=# CREATE FOREIGN TABLE aa_foreign (a int, b int)
postgres=# SERVER postgres_server OPTIONS (table_name 'aa');
CREATE FOREIGN TABLE

Then if you query the foreign table.
postgres=# select * from aa_foreign;
a | b
---+---
1 | 1
1 | 2
1 | 3
(3 rows)

Yeah, done!

This feature still needs more testing, so go ahead and test it by yourself you might be surprised with the things you can do with it.

pageinspect is an extension module of PostgreSQL core allowing to have a look at the contents of relations (index or table) in the database at a low level. In the case of PostgreSQL, tuples of a table are stored in blocks of data whose size can be changed with –with-blocksize at configure step. This module is particularly useful for debugging when implementing a new functionality that changes visibility of data like what could do an autovacuum or map visibility feature, or simply to understand the internals of Postgres without having to read much codea .

Without entering in details in the page and page item structures, be sure to have a look at the documentation about database page layout.

In order to install this module for the source tree, simply do the following.
cd $POSTGRES_ROOT/contrib/pageinspect
make install

Once done, the following files are installed in the share/ folder of your installation path.
pageinspect--1.0.sql pageinspect--unpackaged--1.0.sql pageinspect.control

Since PostgreSQL 9.1, it is necessary to use CREATE EXTENSION to finish the installation of the module. Hence connect to the database and run the following SQL command.
postgres=# CREATE EXTENSION pageinspect;
CREATE EXTENSION

By doing that the following objects are created in
postgres=# \dx+ pageinspect
Objects in extension "pageinspect"
Object Description
------------------------------------------
function bt_metap(text)
function bt_page_items(text,integer)
function bt_page_stats(text,integer)
function fsm_page_contents(bytea)
function get_raw_page(text,integer)
function get_raw_page(text,text,integer)
function heap_page_items(bytea)
function page_header(bytea)
(8 rows)

In those functions, get_raw_page is the most important one because it allows fetching a raw page of data for a given relation. Its output is not that useful as-is, it is however possible to deparse its output to get various readable information.

  • page_header gives information about the page header with general information like the log sequence number (LSN, WAL number) of the last change done on the page, or information about remaining space on page based on its upper and lower offset, tuples item pointer being stored from the top of the page and tuple data+header being stored at the bottom of the page
  • get_raw_page gives information about the tuple items, I’ll come back to that in more details later in this post
  • fsm_page_contents gives an output of the FSM (freespace map, used to locate quickly on which page a tuple can be stored based on the free space available)

There are also additional functions helping to vizualize information about b-trees with bt_metap, bt_page_items and bt_page_stats.

What I really wanted to show in this post is how you can use pageinspect to visualize the changes on your database if you do some simple DML or maintenance operations. So let’s take an example:
postgres=# CREATE TABLE aa AS SELECT 1 AS a;
SELECT 1

Table aa being a fresh relation, its first record has been added in the first page of the relation storage.
postgres=# SELECT lp, lp_len, t_xmin, t_xmax, lp_off from heap_page_items(get_raw_page('aa', 0));
lp | lp_len | t_xmin | t_xmax | lp_off
----+--------+--------+--------+--------
1 | 28 | 685 | 0 | 8160
(1 row)

When doing an INSERT command, what PostgreSQL does is setting the minimum transaction ID from where the tuple becomes visible for other session backends.

Then, how is used the space available on the page? It is possible to know more about that by having a look at the global page information with page_header.
postgres=# SELECT * from page_header(get_raw_page('aa', 0));
lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid
-----------+-----+-------+-------+-------+---------+----------+---------+-----------
0/178D500 | 1 | 0 | 28 | 8160 | 8192 | 8192 | 4 | 0
(1 row)

A page structure in PostgreSQL is particular. At the top of the page 28 bytes are used for the page header information (PageHeaderData in bufpage.h). From the top, item pointers (ItemPointer) of 4 bytes are used for each tuple entry to redirect to the place on page where tuple is located. Tuple header and tuple data are actually stored at the bottom of the page, their length may vary depending on the data stored.

In the case of the first record “1″ of table aa, the lower offset is defined at 28 (position of pointer on page, just after the 28 bytes of the page header). The upper offset shows that 32 bytes are used to store 28 bytes of data and 4 bytes of header. By inserting a second tuple “2″, here is what happens:
postgres=# SELECT * from page_header(get_raw_page('aa', 0));
lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid
-----------+-----+-------+-------+-------+---------+----------+---------+-----------
0/17A1B90 | 1 | 0 | 32 | 8128 | 8192 | 8192 | 4 | 0
(1 row)

4 bytes of ItemPointer data has been added on top of the page and 32 bytes are added to the bottom for the tuple data and header.

After the secind tuple insertion, here is how the page changed.
postgres=# SELECT lp, lp_len, t_xmin, t_xmax, lp_off from heap_page_items(get_raw_page('aa', 0));
lp | lp_len | t_xmin | t_xmax | lp_off
----+--------+--------+--------+--------
1 | 28 | 685 | 0 | 8160
2 | 28 | 687 | 0 | 8128
(2 rows)

In the case of a DELETE, what is done is to update in the page t_xmax for the tuple entry, maximum transaction ID where the tuple is visible.
postgres=# DELETE FROM aa WHERE a = 2;
DELETE 1
postgres=# SELECT lp, lp_len, t_xmin, t_xmax, lp_off from heap_page_items(get_raw_page('aa', 0));
lp | lp_len | t_xmin | t_xmax | lp_off
----+--------+--------+--------+--------
1 | 28 | 685 | 0 | 8160
2 | 28 | 687 | 688 | 8128
(2 rows)

For an UPDATE, what actually happens is an INSERT and a DELETE, a new entry is added in page with a fresh t_min, and the old tuple entry has its t_max updated.
postgres=# UPDATE aa SET a = 3 WHERE a = 1;
UPDATE 1
postgres=# SELECT lp, t_ctid, lp_len, t_xmin, t_xmax, lp_off from heap_page_items(get_raw_page('aa', 0));
lp | t_ctid | lp_len | t_xmin | t_xmax | lp_off
----+--------+--------+--------+--------+--------
1 | (0,3) | 28 | 685 | 689 | 8160
2 | (0,2) | 28 | 687 | 688 | 8128
3 | (0,3) | 28 | 689 | 0 | 8096
(3 rows)

Note also that the CTID of the tuple indicating the couple (pagenumber, tuple position) of the old tuple has been changed to indicate the new tuple inserted.

A last thing, what happens on those pages when performing a VACUUM?
postgres=# vacuum aa;
VACUUM
postgres=# SELECT lp, t_ctid, lp_len, t_xmin, t_xmax, lp_off from heap_page_items(get_raw_page('aa', 0));
lp | t_ctid | lp_len | t_xmin | t_xmax | lp_off
----+--------+--------+--------+--------+--------
1 | | 0 | | | 3
2 | | 0 | | | 0
3 | (0,3) | 28 | 689 | 0 | 8160
(3 rows)
postgres=# SELECT * from page_header(get_raw_page('aa', 0));
lsn | tli | flags | lower | upper | special | pagesize | version | prune_xid
-----------+-----+-------+-------+-------+---------+----------+---------+-----------
0/17A3FA8 | 1 | 5 | 36 | 8160 | 8192 | 8192 | 4 | 0
(1 row)

When running the VACUUM on relation ‘aa’, my session was the only one on the server, so what has been done is removing the tuples seen as dead, as no other sessions would need them. Hence tuple entries 1 and 2 are simply removed and can be used for new fresh tuples. Note also the new value of flags for the page header, before it was set to 1 and now it became 5. In this case the page is set as PD_ALL_VISIBLE, meaning that all the tuples are visible to all the backends.

©2010-2013 Michael Paquier All content is ©Copyright of Otacoo.com 2010-2013. Privacy Policy - Terms of Use