Skip to content

Commit c39fd9e

Browse files
author
Antonin Houska
committed
Updated documentation.
1 parent e3cc931 commit c39fd9e

2 files changed

Lines changed: 109 additions & 75 deletions

File tree

README.md

Lines changed: 108 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
11
# pg_rewrite
22

3-
`pg_rewrite` aims to be a set of tools to perform maintenance tasks which
4-
require a table to be rewritten (i.e. the table data to be copied to a new
5-
storage) and which are expected to limit the access to the table as little as
6-
possible.
3+
`pg_rewrite` is a tool to rewrite table (i.e. to copy its data to a new
4+
file). It allows both read and write access to the table during the rewriting.
75

8-
# INSTALL
6+
Following are the most common reasons to rewrite a table:
7+
8+
1. Change data type of column(s)
9+
10+
Typically this is needed if the existing data type is running out of
11+
values. For example, you may need to change `interger` type to
12+
`bigint`. `ALTER TABLE` command can do that too, but it allows neither
13+
write nor read access to the table during the rewriting.
14+
15+
2. Partition the table
16+
17+
If you realize that your table is getting much bigger than expected and
18+
that partitioning would make your life easier, the next question may be
19+
how to copy the existing data to the new, partitioned table without
20+
stopping all the applications that run DML commands on the table. (When
21+
you decide to use partitioning, the amount of data to copy might already
22+
be significant, so the copying might need a while.)
23+
24+
3. Change order of columns
25+
26+
If you conclude that a different order of columns would save significant
27+
disk space (due to reduced paddding), the problem boils down to copying
28+
data to a new table like in 2). Again, you may need `pg_rewrite` to make
29+
the change smooth.
30+
31+
Note that the following use cases can be combined in a single rewrited.
32+
33+
34+
# INSTALLATION
935

1036
Install PostgreSQL before proceeding. Make sure to have `pg_config` binary,
1137
these are typically included in `-dev` and `-devel` packages. PostgreSQL server
@@ -14,6 +40,7 @@ version 13 or later is required.
1440
```bash
1541
git clone https://github.com/cybertec-postgresql/pg_rewrite.git
1642
cd pg_rewrite
43+
git checkout <the latest stable version>
1744
make
1845
make install
1946
```
@@ -34,28 +61,28 @@ CREATE EXTENSION pg_rewrite;
3461

3562
# USAGE
3663

37-
Assuming you have a table defined like this:
64+
Assume you have a table defined like this
3865

3966
```
4067
CREATE TABLE measurement (
41-
id serial,
68+
id int,
4269
city_id int not null,
4370
logdate date not null,
4471
peaktemp int,
45-
unitsales int,
4672
PRIMARY KEY(id, logdate)
4773
);
4874
```
4975

50-
You need to create a partitioned table having the same columns and data types:
76+
and you need to replace it with a partitioned table. At the same time, you
77+
want to change the data type of the `id` column to `bigint`.
78+
5179

5280
```
5381
CREATE TABLE measurement_aux (
54-
id serial,
82+
id bigint,
5583
city_id int not null,
5684
logdate date not null,
5785
peaktemp int,
58-
unitsales int,
5986
PRIMARY KEY(id, logdate)
6087
) PARTITION BY RANGE (logdate);
6188
```
@@ -73,37 +100,66 @@ CREATE TABLE measurement_y2006m03 PARTITION OF measurement_aux
73100
-- ...
74101
```
75102

76-
*It's essential that both the source (`measurement`) and destination
103+
*It's essential that both the source (`measurement`) and target
77104
(`measurement_aux`) table have an identity index. The easiest way to ensure
78105
this is to create `PRIMARY KEY` or `UNIQUE` constraint. Also note that the key
79-
(i.e. column list) of the identity index of the source and destination table
80-
must be identical. The identity is needed to process data changes that
81-
applications make while data is being copied from the source to the
82-
destination table.*
83-
84-
Also, unless you've set `rewrite.check_constraints` to `false`, make sure that
85-
the destination table has all the constraints that the source table has.
106+
(i.e. column list) of the identity index of the source and target table must
107+
be identical. The identity is needed to process data changes that applications
108+
make while data is being copied from the source to the target table.*
86109

87-
Then, in order to copy the data into the destination table, run the
88-
`partition_table()` function and pass it both the source and destination table,
89-
as well as a new table name for the source table. For example:
110+
Then, in order to copy the data into the target table, run the
111+
`rewrite_table()` function and pass it both the source and target table, as
112+
well as a new table name for the source table. For example:
90113

91114
```
92-
SELECT partition_table('measurement', 'measurement_aux', 'measurement_old');
115+
SELECT rewrite_table('measurement', 'measurement_aux', 'measurement_old');
93116
```
94117

95-
The call will copy data from `measurement` to `measurement_aux`, then it will
96-
lock `measurement` exclusively and rename (1) `measurement` to
97-
`measurement_old`, (2) `measurement_aux` to `measurement`. Thus `measurement`
98-
ends up to be the partitioned table, while `measurement_old` is the original,
99-
non-partitioned table.
118+
The call will first copy all rows from `measurement` to `measurement_aux`. The
119+
it will apply to `measurement_aux` all the data changes (INSERT, UPDATE,
120+
DELETE) that took place in `measurement` during the copying. Next, it will
121+
lock `measurement` so that neither read nor write access is possible. Finally
122+
it will rename `measurement` to `measurement_old` and `measurement_aux` to
123+
`measurement`. Thus `measurement` ends up to be the partitioned table, while
124+
`measurement_old` is the original, non-partitioned table.
125+
126+
If a column of the target table has a different data type from the
127+
corresponding column of the source table, an implicit or assignment cast must
128+
exist between the two types.
129+
130+
# Constraints
131+
132+
The target table should obviously have the same constraints as the source
133+
table. It's recommended to handle constraints creation this way:
134+
135+
1. Add PRIMARY KEY, UNIQUE and EXCLUDE constraints of the source table to the
136+
target table before you call `rewrite_table()`. These are enforced during
137+
the rewriting, so any violation would make `rewrite_table()` fail
138+
(ROLLBACK). (The constraints must have been enforced in the source table,
139+
but it does not hurt to check them in the target table, especially if the
140+
column data type is being changed.)
141+
142+
2. Add NOT NULL constraints. `rewrite_table()` by-passes validation of these,
143+
but all the rows it inserts into the target table must have been validated
144+
in the source table. Even if the column data tape is different in the
145+
target table, the data type conversion should not turn non-NULL value to
146+
NULL or vice versa.
147+
148+
3. CHECK and FOREIGN KEY are created automatically by `rewrite_table()` when
149+
all the data changes have been applied to the target table. However, these
150+
constraints are created as NOT VALID, so you need to use the `ALTER TABLE
151+
... VALIDATE CONSTRAINT ...` command to validate them.
152+
153+
Of course, the function could create the constraints immediately as valid,
154+
but that would imply blocking access to the table for significant time.
155+
100156

101157
# Progress monitoring
102158

103-
If `partition_table()` takes long time to finish, you might be interested in
104-
the progress. The `pg_rewrite_progress` view shows all the pending calls of
105-
the function in the current database. The `src_table`, `dst_table` and
106-
`src_table_new` columns contain the arguments of the `partition_table()`
159+
If `rewrite_table()` takes long time to finish, you might be interested in the
160+
progress. The `pg_rewrite_progress` view shows all the pending calls of the
161+
function in the current database. The `src_table`, `dst_table` and
162+
`src_table_new` columns contain the arguments of the `rewrite_table()`
107163
function. `ins_initial` is the number of tuples inserted into the new table
108164
storage during the "initial load stage", i.e. the number of tuples present in
109165
the table before the processing started. On the other hand, `ins`, `upd` and
@@ -113,44 +169,29 @@ incorporated into the partitioned table, otherwise they'd get lost.)
113169

114170
# Limitations
115171

116-
Please consider the following before you try to use the function:
117-
118-
* Foreign table partitions are not supported.
119-
120-
* It's not expected that the table that you try to partition is referenced by
121-
any foreign key. The problem is that the destination table is initially
122-
empty, so you won't be able to create the foreign keys that reference it.
172+
If the target table is partitioned, it's not allowed to have foreign
173+
tables as partitions.
123174

124175
# Configuration
125176

126177
Following is the description of the configuration variables that affect
127178
behavior of the functions of this extension.
128179

129-
* `rewrite.check_constraints`
130-
131-
Before copying of the data starts, it's checked whether the destination table
132-
has the same constraints as the source table, and throws an ERROR if a
133-
difference is found. The point is that due to (accidentally) missing
134-
constraint on the destination table, data that violate constraints on the
135-
source table would be allowed to appear in the destination table as soon as
136-
the processing is finished. Even an extra constraint on the destination table
137-
is a problem because the extension only assumes that all the data it copies do
138-
satisfy constraints on the source table, however it does not validate them
139-
against the additional constraints on the destination table.
140-
141-
By setting `rewrite.check_constraints` to `false`, the user can turn off the
142-
constraint checks. Please be very cautions before you do so.
143-
144-
The default value is `true`.
145-
146180
* `rewrite.max_xlock_time`
147181

148182
Although the table being processed is available for both read and write
149183
operations by other transactions most of the time, an exclusive lock is needed
150-
to finalize the processing (i.e. to process the remaining concurrent changes
151-
and to rename the tables). If the extension function seems to block access to
152-
tables too much, consider setting `rewrite.max_xlock_time` GUC parameter. For
153-
example:
184+
to finalize the processing (i.e. to do the table renaming), which blocks both
185+
read and write access. This should take very short time that users should
186+
harly notice.
187+
188+
However, if a significant amount of changes took place in the source table
189+
while the extension was waiting for the (exclusive) lock, the outage might
190+
take proportionally longer time. The point is that those changes need to be
191+
propagated to the target table before the exclusive lock can be released.
192+
193+
If the extension function seems to block access to tables too much, consider
194+
setting `rewrite.max_xlock_time` GUC parameter. For example:
154195

155196
```
156197
SET rewrite.max_xlock_time TO 100;
@@ -169,21 +210,14 @@ it needs.
169210

170211
# Concurrency
171212

172-
1. The extension does not prevent other transactions from altering table at
173-
certain stages of the processing. If a `disruptive command` (i.e. `ALTER
174-
TABLE`) manages to commit before the processing could finish, the table
175-
processing aborts and all changes done are rolled back.
213+
1. While the rewrite_table() function is executing, `ALTER TABLE` command on
214+
the same table should be blocked until the rewriting is done. However, in
215+
some cases the `ALTER TABLE` command and the rewrite_table() function might
216+
end up in a deadlock. Therefore it's recommended not to run ALTER TABLE on
217+
a table which is being rewritten.
176218

177-
2. The functions of this extension allow for MVCC-unsafe behavior described in
219+
2. The `rewrite_table()` function allows for MVCC-unsafe behavior described in
178220
the first paragraph of [mvcc-caveats][1].
179221

180-
# Locking
181-
182-
Since the table renaming requires an exclusive lock, applications won't be able
183-
to access the table that you try to process for very short time. However, if a
184-
significant amount of changes took place in the source table while the
185-
extension was waiting for the lock, the outage will take proportionally longer
186-
time. The point is that those changes need to be propagated to the destination
187-
table before the exclusive lock can be released.
188222

189-
[1]: https://www.postgresql.org/docs/13/static/mvcc-caveats.html
223+
[1]: https://www.postgresql.org/docs/current/mvcc-caveats.html

pg_rewrite.control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pg_rewrite extension
2-
comment = 'Tools for maintenance that requires table rewriting.'
2+
comment = 'Tool for maintenance that requires table rewriting.'
33
default_version = '1.3'
44
module_pathname = '$libdir/pg_rewrite'
55
relocatable = true

0 commit comments

Comments
 (0)