No caso mais simples, quando inserimos uma nova linha em uma tabela (e a transação é confirmada), ela fica visível para todas as transações subseqüentes. Veja xmax
sendo 0 neste exemplo:
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
Quando o atualizamos (porque o sinalizador foi definido FALSE
por acidente), ele muda um pouco:
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 acordo com o modelo MVCC que o PostgreSQL usa, uma nova linha física foi gravada e a antiga invalidada (isso pode ser visto no ctid
). O novo ainda está visível para todas as transações subseqüentes.
Agora, há uma coisa interessante acontecendo quando revertemos o 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
A versão da linha permanece a mesma, mas agora xmax
está definida como algo. Apesar disso, as transações subsequentes podem ver esta linha (caso contrário, inalterada).
Depois de ler um pouco sobre isso, você pode descobrir algumas coisas sobre a visibilidade da linha. Existe o mapa de visibilidade , mas apenas informa se uma página inteira está visível - definitivamente não funciona no nível da linha (tupla). Depois, há o log de confirmação (aka clog
) - mas como o Postgres descobre se precisa visitá-lo?
Decidi dar uma olhada nos bits da máscara de informação para descobrir como a visibilidade realmente funciona. Para vê-los, a maneira mais fácil é usar a extensão pageinspect . Para descobrir quais bits estão definidos, criei uma tabela para armazená-los:
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));
Em seguida, verifiquei o que está dentro da minha vis
tabela - observe que pageinspect
mostra o conteúdo físico da pilha, para que não sejam retornadas apenas as linhas visíveis:
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
O que entendi acima é que a primeira versão ganhou vida com a transação 2699, substituída com sucesso pela nova versão em 2700.
Em seguida, a próxima, que estava viva desde 2700, teve uma tentativa de retroceder UPDATE
em 2702, vista de HEAP_XMAX_INVALID
.
O último nunca realmente nasceu, como mostra HEAP_XMIN_INVALID
.
Portanto, considerando o exposto acima, o primeiro e o último caso são óbvios - eles não são mais visíveis para a transação 2703 ou superior.
O segundo deve ser procurado em algum lugar - suponho que seja o log de confirmação, também conhecido como clog
.
Para complicar ainda mais os problemas, um UPDATE
resultado subsequente é o seguinte:
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
Aqui já vejo dois candidatos que podem ser visíveis. Então, finalmente, aqui estão minhas perguntas:
- É minha suposição de que
clog
é o lugar para procurar determinar a visibilidade nesses casos? - Quais sinalizadores (ou combinação de sinalizadores) indicam ao sistema para visitar o
clog
? - Existe uma maneira de examinar o que há dentro
clog
? Há menções sobreclog
corrupção nas versões anteriores do Postgres e uma dica de que se pode criar um arquivo falso manualmente. Esta informação ajudaria muito com isso.