domingo, 7 de agosto de 2016

Esperando a 9.0 -- WAITING FOR 9.0 – PG_UPGRADE

Esta entrada es una traducción de https://www.depesz.com/2010/05/19/waiting-for-9-0-pg_upgrade/
La traducción puede contener errores.
This entry is a translation of https://www.depesz.com/2010/05/19/waiting-for-9-0-pg_upgrade/
The translation may contain errors.

El 12 de mayo, Bruce Monjian subió un nuevo módulo de contribución para la 9.0 - pg_upgrade.

Según entiendo - esto es lo que estaba disponible antes como pg-migrator.

Si tú no estás familiarizado con ello - es una herramienta que permite hacer un upgrade (subir versión) de $PGDATA desde alguna versión a otra versión. ¿Cuál es el caso de uso? Asumamos que tú tienes esta base de datos de 200 GB funcionando en 8.3, y te gustaría ir a 8.4 (o 9.0). El camino normal es pg_dump + pg_restore - el cual llevará algún tiempo. Con pg-migrate/pg_upgrade esto debe de ser más rápido, y más fácil. Así, vamos a jugar con ello.

Yo tengo 2 versiones de Pg instaladas:
8.3.10
9.0 (directo desde git, se presenta a sí misma como 9.0beta1)

Lo primero, vamos a crear una base de datos pruebas en 8.3. Como yo no tengo la base de datos aún (es una instalación de pruebas), yo crearé una:

=$ source pg-env.sh 8.3.10

Pg version 8.3.10 chosen

=$ mkdir /home/pgdba/data-8.3

=$ initdb -D /home/pgdba/data-8.3/ -E UTF-8
...
Success. You can now start the database server using:
...

=$ pg_ctl -D /home/pgdba/data-8.3 -l /home/pgdba/logfile-8.3 start
server starting

OK, aquí yo tengo una buena 8.3 funcionando:

=$ psql -d postgres -c 'select version()'
                                             version
--------------------------------------------------------------------------------------------------
 PostgreSQL 8.3.10 on x86_64-unknown-linux-gnu, compiled by GCC gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
(1 row)


Así vamos a añadir algunos datos a ella:

# create table test ( i int4, j int8, t text, ts timestamptz, ip inet);
CREATE TABLE

# insert into test (i, j, t, ts, ip)
    select
        random() * 1000000000,
        random() * 8000000000,
        repeat('depesz : ', cast(5 + random() * 10 as int4)),
        now() - random() * '5 years'::interval,
        '127.0.0.1'
    from generate_series(1, 10000000);
INSERT 0 10000000

OK. Datos dentro, vamos a ver los datos y algunas estadísticas, para ser capaz de verificar los datos después de la migración:

<code># select * from test limit 3;
     i     |     j      |                                                      t                                                       |              ts               |    ip
-----------+------------+--------------------------------------------------------------------------------------------------------------+-------------------------------+-----------
 154912674 | 7280213505 | depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz :                                      | 2007-07-25 20:55:11.357501+02 | 127.0.0.1
 560106405 | 7676185891 | depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz :  | 2006-07-23 15:40:35.203901+02 | 127.0.0.1
 683442113 | 5831204534 | depesz : depesz : depesz : depesz : depesz : depesz : depesz : depesz :                                      | 2006-07-01 16:58:17.175101+02 | 127.0.0.1
(3 rows)</code>


# select
    min(i), count(distinct i), max(i), sum(i),
    min(j), count(distinct j), max(j), sum(j),
    count(distinct t), sum(length(t)),
    count(distinct ts),
    count(*),
    pg_relation_size('test'),
    pg_total_relation_size('test')
from test;
-[ RECORD 1 ]------------------------------
min                    | 16
count                  | 9948511
max                    | 999999620
sum                    | 5001298277187874
min                    | 417
count                  | 9976964
max                    | 7999999207
sum                    | 40006841224204502
count                  | 11
sum                    | 900027558
count                  | 9968978
count                  | 10000000
pg_relation_size       | 1563410432
pg_total_relation_size | 1563418624


OK, Ahora, vamos a añadir algunos índices para estar seguros que estos están también funcionando después de la migración:

# create index i1 on test (i);
CREATE INDEX
# create index i2 on test (j);
CREATE INDEX
# create index i3 on test (ts);
CREATE INDEX
# select min(i), max(i), min(j), max(j), min(ts), max(ts) from test;
-[ RECORD 1 ]----------------------
min | 16
max | 999999620
min | 417
max | 7999999207
min | 2005-05-13 15:03:01.027901+02
max | 2010-05-12 15:02:48.586301+02
# explain analyze select min(i), max(i), min(j), max(j), min(ts), max(ts) from test;
                                                                  QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=22.35..22.36 rows=1 width=0) (actual time=0.113..0.113 rows=1 loops=1)
   InitPlan
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.023..0.023 rows=1 loops=1)
           ->  Index Scan using i1 on test  (cost=0.00..37258844.64 rows=10000000 width=20) (actual time=0.022..0.022 rows=1 loops=1)
                 Filter: (i IS NOT NULL)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.008..0.008 rows=1 loops=1)
           ->  Index Scan Backward using i1 on test  (cost=0.00..37258844.64 rows=10000000 width=20) (actual time=0.007..0.007 rows=1 loops=1)
                 Filter: (i IS NOT NULL)
     ->  Limit  (cost=0.00..3.72 rows=1 width=20) (actual time=0.012..0.012 rows=1 loops=1)
           ->  Index Scan using i2 on test  (cost=0.00..37236683.21 rows=10000000 width=20) (actual time=0.011..0.011 rows=1 loops=1)
                 Filter: (j IS NOT NULL)
     ->  Limit  (cost=0.00..3.72 rows=1 width=20) (actual time=0.006..0.006 rows=1 loops=1)
           ->  Index Scan Backward using i2 on test  (cost=0.00..37236683.21 rows=10000000 width=20) (actual time=0.006..0.006 rows=1 loops=1)
                 Filter: (j IS NOT NULL)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.049..0.049 rows=1 loops=1)
           ->  Index Scan using i3 on test  (cost=0.00..37257725.52 rows=10000000 width=20) (actual time=0.048..0.048 rows=1 loops=1)
                 Filter: (ts IS NOT NULL)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.008..0.008 rows=1 loops=1)
           ->  Index Scan Backward using i3 on test  (cost=0.00..37257725.52 rows=10000000 width=20) (actual time=0.007..0.007 rows=1 loops=1)
                 Filter: (ts IS NOT NULL)
 Total runtime: 0.192 ms
(21 rows)

OK. Todo parece estár funcionando. Antes de que yo realmente pruebe a migrarla, yo primero haré un backup del $PGDATA de 8.3 - sólo para estar seguro de que yo no tengo que re-generar los datos.

=$ pg_ctl -D /home/pgdba/data-8.3/ stop
waiting for server to shut down.... done
server stopped

=$ rsync -a --delete --delete-after data-8.3/ data-8.3.backup/

=$ for a in data-8.3*; do printf "%-30s : " $a; find $a/ -type f -print | sort | xargs cat | md5sum -; done
data-8.3                       : a559d351b54f20ce66a0bb89a0724eb9  -
data-8.3.backup                : a559d351b54f20ce66a0bb89a0724eb9  -


Así, no vamos a intentar actualizarla a 9.0. Según la documentación yo necesito primero crear el cluster de destino. Así, vamos a hacerlo:

=$ source pg-env.sh 9.0
Pg version 9.0 chosen

=$ initdb -D /home/pgdba/data-9.0
...
Success. You can now start the database server using:


Antes de que yo vaya, vamos a comprobar el md5sum de data-9.0:

=$ for a in data-9.0; do printf "%-30s : " $a; find $a/ -type f -print | sort | xargs cat | md5sum -; done
data-9.0                       : 930fdef9c4c48808a9dbabe8573b2d2c  -

Ahora, que yo tengo ambos directorio de datos listos, y ninguno de los pg ejecutándose (ellos no pueden estar ejecutándose durante el upgrade, yo puedo:

=$ time pg_upgrade
    --old-datadir=/home/pgdba/data-8.3/ \
    --new-datadir=/home/pgdba/data-9.0/ \
    --old-bindir=/opt/pgsql-8.3.10/bin/ \
    --new-bindir=/opt/pgsql-9.0/bin/ \
    --old-port=5830 \
    --new-port=5900 \
    --user=pgdba

Performing Consistency Checks
-----------------------------
Checking old data directory (/home/pgdba/data-8.3)          ok
Checking new data directory (/home/pgdba/data-9.0)          ok
Checking for /contrib/isn with bigint-passing mismatch      ok
Checking for invalid 'name' user columns                    ok
Checking for tsquery user columns                           ok
Creating script to adjust sequences                         ok
Checking for large objects                                  ok
Creating catalog dump                                       ok
Checking for presence of required libraries                 ok

| If pg_upgrade fails after this point, you must
| re-initdb the new cluster before continuing.
| You will also need to remove the ".old" suffix
| from /home/pgdba/data-8.3/global/pg_control.old.

Performing Migration
--------------------
Adding ".old" suffix to old global/pg_control               ok
Analyzing all rows in the new cluster                       ok
Freezing all rows on the new cluster                        ok
Deleting new commit clogs                                   ok
Copying old commit clogs to new server                      ok
Setting next transaction id for new cluster                 ok
Resetting WAL archives                                      ok
Setting frozenxid counters in new cluster                   ok
Creating databases in the new cluster                       ok
Adding support functions to new cluster                     ok
Restoring database schema to new cluster                    ok
Removing support functions from new cluster                 ok
Restoring user relation files
                                                            ok
Setting next oid for new cluster                            ok
Creating script to delete old cluster                       ok
Checking for tsvector user columns                          ok
Checking for hash and gin indexes                           ok
Checking for bpchar_pattern_ops indexes                     ok
Checking for large objects                                  ok

Upgrade complete
----------------
| Optimizer statistics and free space information
| are not transferred by pg_upgrade so consider
| running:
|       vacuumdb --all --analyze
| on the newly-upgraded cluster.

| Running this script will delete the old cluster's data files:
|       /home/pgdba/pg_upgrade_output/delete_old_cluster.sh

real    0m28.910s
user    0m0.160s
sys     0m5.660s

Comprobación rápida:

=$ for a in data-8.3* data-9.0; do printf "%-30s : " $a; find $a/ -type f -print | sort | xargs cat | md5sum -; done
data-8.3                       : 2d3e26f3e7363ec225fb1f9f93e45184  -
data-8.3.backup                : a559d351b54f20ce66a0bb89a0724eb9  -
data-9.0                       : 40ccb32c89acafb5436ea7dcd9f737a5  -

Muetra que ambos fuente y destinos han sido modificados. Así, vamos a arrancar la 9.0 y ver los datos:

=$ pg_ctl -D /home/pgdba/data-9.0 -l logfile-9.0 start
server starting

Por supuesto - yo debo de ejecutar vacuum como sugerido por pg_upgrade:

=$ vacuumdb --all --analyze
vacuumdb: vacuuming database "postgres"
WARNING:  some databases have not been vacuumed in over 2 billion transactions
DETAIL:  You might have already suffered transaction-wraparound data loss.
vacuumdb: vacuuming database "template1"
WARNING:  some databases have not been vacuumed in over 2 billion transactions
DETAIL:  You might have already suffered transaction-wraparound data loss.

El error de arriba parece malo, pero esto ha sido recientemente discutivo en pgsql-hackers, y yo creo que será corregido antes de la 9.0 final sea liberada.

¿Qué hay acerca de nuestros datos reales?

# select
    min(i), count(distinct i), max(i), sum(i),
    min(j), count(distinct j), max(j), sum(j),
    count(distinct t), sum(length(t)),
    count(distinct ts),
    count(*),
    pg_relation_size('test'),
    pg_total_relation_size('test')
from test;
-[ RECORD 1 ]----------+------------------
min                    | 16
count                  | 9948511
max                    | 999999620
sum                    | 5001298277187874
min                    | 417
count                  | 9976964
max                    | 7999999207
sum                    | 40006841224204502
count                  | 11
sum                    | 900027558
count                  | 9968978
count                  | 10000000
pg_relation_size       | 1563410432
pg_total_relation_size | 2237767680


Todos los valores son los mismos excepto para pg_total_relation_size, pero estos es porque yo añadí índices en 8.3 antes de obtener pg_total_relation_size(), así que está bien.

Ahora. ¿Serán usados los índices?

# explain analyze select min(i), max(i), min(j), max(j), min(ts), max(ts) from test;
                                                                  QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=22.36..22.37 rows=1 width=0) (actual time=0.319..0.319 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.066..0.077 rows=1 loops=1)
           ->  Index Scan using i1 on test  (cost=0.00..37258941.16 rows=10000000 width=20) (actual time=0.039..0.039 rows=1 loops=1)
                 Index Cond: (i IS NOT NULL)
   InitPlan 2 (returns $1)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.042..0.051 rows=1 loops=1)
           ->  Index Scan Backward using i1 on test  (cost=0.00..37258941.16 rows=10000000 width=20) actual time=0.023..0.023 rows=1 loops=1)
                 Index Cond: (i IS NOT NULL)
   InitPlan 3 (returns $2)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.039..0.046 rows=1 loops=1)
           ->  Index Scan using i2 on test  (cost=0.00..37258751.12 rows=10000000 width=20) (actual time=0.025..0.025 rows=1 loops=1)
                 Index Cond: (j IS NOT NULL)
   InitPlan 4 (returns $3)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.031..0.037 rows=1 loops=1)
           ->  Index Scan Backward using i2 on test  (cost=0.00..37258751.12 rows=10000000 width=20) (actual time=0.018..0.018 rows=1 loops=1)
                 Index Cond: (j IS NOT NULL)
   InitPlan 5 (returns $4)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.016..0.017 rows=1 loops=1)
           ->  Index Scan using i3 on test  (cost=0.00..37259259.58 rows=10000000 width=20) (actual time=0.015..0.015 rows=1 loops=1)
                 Index Cond: (ts IS NOT NULL)
   InitPlan 6 (returns $5)
     ->  Limit  (cost=0.00..3.73 rows=1 width=20) (actual time=0.009..0.009 rows=1 loops=1)
           ->  Index Scan Backward using i3 on test  (cost=0.00..37259259.58 rows=10000000 width=20) (actual time=0.009..0.009 rows=1 loops=1)
                 Index Cond: (ts IS NOT NULL)
 Total runtime: 0.403 ms
(26 rows)

Sí. Funciona.

Actualmente hay algunos cortes ásperos, a saber:

algo sobre transaction wraparound y template0
falta de documentación en formato html

pero yo creo que esto será pronto corregido. Y la velocidad es muy impresionante - 2.2 GB de $PGDATA convertidos en 29 segundos es muy guay.


No hay comentarios:

Publicar un comentario