En el caso más simple, cuando insertamos una nueva fila en una tabla (y la transacción se confirma), será visible para todas las transacciones posteriores. Ver xmax
siendo 0 en este ejemplo:
CREATE TABLE vis (
id serial,
is_active boolean
);
INSERT INTO vis (is_active) VALUES (FALSE);
SELECT ctid, xmin, xmax, * FROM vis;
ctid │xmin │ xmax │ id │ is_active
───────┼─────┼──────┼────┼───────────
(0,1) │2699 │ 0 │ 1 │ f
Cuando lo actualizamos (porque el indicador se configuró FALSE
por accidente), cambia un poco:
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
──────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 0 │ 1 │ t
De acuerdo con el modelo MVCC que utiliza PostgreSQL, se escribió una nueva fila física y se anuló la anterior (esto se puede ver desde ctid
). El nuevo todavía es visible para todas las transacciones posteriores.
Ahora sucede algo interesante cuando retrocedemos UPDATE
:
BEGIN;
UPDATE vis SET is_active = TRUE;
ROLLBACK;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
───────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 2702 │ 1 │ t
La versión de fila permanece igual, pero ahora xmax
está configurada en algo. A pesar de esto, las transacciones posteriores pueden ver esta fila (sin cambios).
Después de leer un poco sobre esto, puede descubrir algunas cosas sobre la visibilidad de la fila. Existe el mapa de visibilidad , pero solo indica si una página completa es visible; definitivamente no funciona en el nivel de fila (tupla). Luego está el registro de confirmación (aka clog
), pero ¿cómo se da cuenta Postgres si tiene que visitarlo?
Decidí echar un vistazo a los bits de infomask para descubrir cómo funciona realmente la visibilidad. Para verlos, la forma más fácil es usar la extensión pageinspect . Para saber qué bits están configurados, creé una tabla para almacenarlos:
CREATE TABLE infomask (
i_flag text,
i_bits bit(16)
);
INSERT INTO infomask
VALUES
('HEAP_HASNULL', x'0001'::bit(16)),
('HEAP_HASVARWIDTH', x'0002'::bit(16)),
('HEAP_HASEXTERNAL', x'0004'::bit(16)),
('HEAP_HASOID', x'0008'::bit(16)),
('HEAP_XMAX_KEYSHR_LOCK', x'0010'::bit(16)),
('HEAP_COMBOCID', x'0020'::bit(16)),
('HEAP_XMAX_EXCL_LOCK', x'0040'::bit(16)),
('HEAP_XMAX_LOCK_ONLY', x'0080'::bit(16)),
('HEAP_XMIN_COMMITTED', x'0100'::bit(16)),
('HEAP_XMIN_INVALID', x'0200'::bit(16)),
('HEAP_XMAX_COMMITTED', x'0400'::bit(16)),
('HEAP_XMAX_INVALID', x'0800'::bit(16)),
('HEAP_XMAX_IS_MULTI', x'1000'::bit(16)),
('HEAP_UPDATED', x'2000'::bit(16)),
('HEAP_MOVED_OFF', x'4000'::bit(16)),
('HEAP_MOVED_IN', x'8000'::bit(16)),
('HEAP_XACT_MASK', x'FFF0'::bit(16));
Luego verifiqué lo que hay dentro de mi vis
tabla: tenga en cuenta que pageinspect
muestra el contenido físico del montón, por lo que no solo se devuelven las filas visibles:
SELECT t_xmin, t_xmax, string_agg(i_flag, ', ') FILTER (WHERE (t_infomask::bit(16) & i_bits)::integer::boolean)
FROM heap_page_items(get_raw_page('vis', 0)),
infomask
GROUP BY t_xmin, t_xmax;
t_xmin │ t_xmax │ string_agg
────────┼────────┼──────────────────────────────────────────────────────
2699 │ 2700 │ HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
2700 │ 2702 │ HEAP_XMIN_COMMITTED, HEAP_XMAX_INVALID, HEAP_UPDATED
2702 │ 0 │ HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
Lo que entiendo de lo anterior es que la primera versión cobró vida con la transacción 2699, luego se reemplazó con éxito por la nueva versión a las 2700.
Luego, la siguiente, que estaba viva desde 2700, tuvo un intento de UPDATE
reversión en 2702, visto desde HEAP_XMAX_INVALID
.
El último nunca nació realmente, como lo demuestra HEAP_XMIN_INVALID
.
Entonces, adivinando lo anterior, el primer y el último caso son obvios: ya no son visibles para la transacción 2703 o superior.
El segundo tiene que buscarse en alguna parte, supongo que es el registro de confirmación, también conocido como clog
.
Para complicar aún más los problemas, un UPDATE
resultado posterior en lo siguiente:
t_xmin │ t_xmax │ string_agg
────────┼────────┼────────────────────────────────────────────────────
2699 │ 2700 │ HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
2702 │ 0 │ HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
2703 │ 0 │ HEAP_XMAX_INVALID, HEAP_UPDATED
2700 │ 2703 │ HEAP_XMIN_COMMITTED, HEAP_UPDATED
Aquí ya veo dos candidatos que podrían ser visibles. Entonces, finalmente, aquí están mis preguntas:
- ¿Es mi suposición de que
clog
es el lugar para mirar para determinar la visibilidad en estos casos? - ¿Qué banderas (o combinación de banderas) le dicen al sistema que visite
clog
? - ¿Hay alguna manera de examinar lo que hay dentro del
clog
? Hay menciones sobre laclog
corrupción en versiones anteriores de Postgres y una pista de que uno puede construir un archivo falso manualmente. Esta información ayudaría mucho con ella.