Skip to content

Version 1.1: Add support of transactions and savepoints #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 25, 2018

Conversation

CherkashinSergey
Copy link
Collaborator

No description provided.

@funbringer funbringer self-requested a review April 13, 2018 16:20
Makefile Outdated
@@ -4,10 +4,10 @@ MODULE_big = pg_variables
OBJS = pg_variables.o pg_variables_record.o $(WIN32RES)

EXTENSION = pg_variables
DATA = pg_variables--1.0.sql
DATA = pg_variables--1.0.sql pg_variables--1.1.sql pg_variables--1.0--1.1.sql
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не все, что нужно изменить. Надо добавить новую переменную с текущей версией расширения, и собирать последний pg_variables-X.Y.sql при помощи cat в отдельном правиле.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CherkashinSergey собирать последний pg_variables-1.1.sql не нужно. Сейчас так не делают. Достаточно сделать дельта файл pg_variables--1.0--1.1.sql. Для примера можешь посмотреть модуль adminpack в PostgreSQL master ветки. В общем тебе достаточно удалить файл pg_variables-1.1.sql.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@za-arthur это не будет работать со старыми версиями постгреса.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, точно. Тогда этот файл нужен.

Copy link
Collaborator

@funbringer funbringer Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я думаю, генерация при помощи cat не является большой проблемой. Все-таки 9.5 и 9.6 довольно популярны, и способ будет совместим с будущими версиями. Для генерации оптимально оставить файл для 1.0 и конкатенировать его последовательно со всеми апдейтами. В таком случае свежая установка будет приводить у тому же результату, что и обновление.

@za-arthur правильно заметил, что контрибы по определению избавлены от этой проблемы.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В винде нет проблем с ним?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В pg_pathman вроде как раз и так сделано. Тогда можно перенять опыт.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хороший вопрос, у нашей сборки проблем не будет, потому что генератор Виктора умеет работать с таким правилом. Насчет ваниллы не уверен.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vbwagner как сейчас принято решать эту задачу?

@@ -0,0 +1,156 @@
/* contrib/pg_variables/pg_variables--1.0.sql */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этого файла не должно быть в репозитории, он должен генерироваться автоматически (добавить в .gitignore).

MemoryContext packageContext);

/* Internal macros to manage with dlist structure */
#define get_actual_value_scalar(variable) \

This comment was marked as resolved.

This comment was marked as resolved.


/* All records will be freed */
MemoryContextDelete(variable->value.record.hctx);
MemoryContextDelete(record->hctx);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В функции clean_records() осуществляется удаление tuple descriptor, хэш-таблицы и memory context'а. Этот код можно улучить: хэш-таблица и tuple descriptor уже "живут" в этом контексте, поэтому достаточно просто удалить контекст.

Это позволит избавится от хрупкого кода удаления объектов в других местах, например, в release_savepoint() и clean_records():

if (variable->typid == RECORDOID)
{
	hash_destroy(historyEntryToDelete->value.record.rhash);
	FreeTupleDesc(historyEntryToDelete->value.record.tupdesc);
	/* Код выше будет не нужен */
	MemoryContextDelete(historyEntryToDelete->value.record.hctx);
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Согласен. Можно. Но тут я действовал по принципу наименьшего вмешательства и оставил алгоритм таким, какой он был. Если считаешь, что надо всё-таки убрать лишние удаления - то давай.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Принцип наименьшего вмешательства имеет право на существование, но время и место для улучшения удачные :) Я думаю, будет лучше сразу улучшать небольшие фрагменты кода.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да вызывать MemoryContextDelete() только один раз намного логичнее.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ну, в таком случае я ещё уберу вызов hash_sestroy() из remove_packages() и remove_package(). Это выглядит легальным.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CherkashinSergey хорошая идея!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Возможно еще стоит его убрать из release_savepoint().

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, само собой

pg_variables.c Outdated

static void
freeChangedVars()
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь особенно заметны проблемы с форматированием. При форматировании кода надо придерживаться стиля PostgreSQL. В разных местах проскакивают лишние пробелы и табуляция.

pg_variables.c Outdated
cvnToDelete = dlist_container( ChangedVarsNode,
node,
dlist_pop_head_node(changedVars));
pfree(cvnToDelete);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь производится очистка через цикл + pfree(). В принципе, можно было бы разместить список в отдельном мемори контексте и при необходимости просто выполнять MemoryContextReset().

/* Release memory for variable */
if (scalar->typbyval == false && scalar->is_null == false)
pfree(DatumGetPointer(scalar->value));

scalar->is_null = is_null;
if (!scalar->is_null)
{
MemoryContext oldcxt;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем перемещен код для работы с mcxt?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Потому что внутри getVariableByNameWithTypeAndTrans() выделяется память с помощь palloc0(), но работы с контекстом там нет.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В таком случае имеет смысл сразу выделять память из контекста. Чем дальше функции переключения контекста находятся от кода, который в них нуждается, тем сложнее его читать и находить ошибки.

Copy link
Contributor

@za-arthur za-arthur Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется нормально. Код не сложный, модуль и код не большой и использование контекстов тут понятно. В данном случае я сразу понял, почему контекст был перемещен.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я согласен, но вообще есть функция MemoryContextAllocZero(), которая позволяет напрямую аллоцировать из контекста. Впрочем, я ни на чем не настаиваю :)

pg_variables.c Outdated

/*
* Intercept execution during subtransaction processing
* Since sub-transactions are created less often, but can have several levels
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вообще они происходят довольно часто.

pg_variables.c Outdated
{
switch (event){
case SUBXACT_EVENT_START_SUB:
apply_action_on_variables(CREATE_SAVEPOINT_SUB);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я думаю, что менять все переменные при старте субтранзакции - это неудачная идея. Если их будет довольно много, то производительность сильно просядет. Было бы эффективнее вести учет переменных, которые хотя бы один раз менялись в рамках транзакции. Полная итерация по хеш-таблицам не эффективна.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Написал хрень в комментарии, удалил, пишу заново :)

Вижу, что используется список измененных переменных, но его же имеет смысл юзать для действий над переменными в субтранзакциях, вместо того, чтобы бежать по всем хеш-таблицам.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Дима тут оказывается уже написал про копирование переменных :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это я ещё в субботу сделал, просто не отписался. Извините.

pg_variables.c Outdated
/*
* Possible actions on variables
*/
enum Action
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь не хватает typedef.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сделал.

@funbringer
Copy link
Collaborator

Местами код оформлен небрежно, было бы неплохо привести его к общему знаменателю.

@funbringer

This comment has been minimized.

README.md Outdated
SELECT pgv_set('pack', 'var_text', 'before savepoint'::text, true);
SAVEPOINT sp1;
SELECT pgv_set('pack', 'var_text', 'savepoint sp1'::text, true);
SELECT pgv_get('pack', 'var_text', NULL::text);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут можно добавить вывод pgv_get().

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Думаю, тут стоит убрать сам вызов pgv_get(). По идее и так понятно, что в этот момент лежит в переменной.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, ты прав. Вызов pgv_set() тут никакого смысла не несет.

pg_variables.c Outdated
{
MemoryContext oldcxt;
Assert(ModuleContext && changedVars);
oldcxt = MemoryContextSwitchTo(ModuleContext);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А вообще, тут MemoryContextSwitchTo() не нужен. Т.к. никакого выделения памяти не происходит, для pfree() он не нужен.

pg_variables.c Outdated
MemoryContext oldcxt;
dlist_mutable_iter miter;
Assert(ModuleContext && changedVars);
oldcxt = MemoryContextSwitchTo(ModuleContext);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут MemoryContextSwitchTo() также не нужен, по-моему.

README.md Outdated
SELECT pgv_set('pack', 'var_text', 'before savepoint'::text, true);
SAVEPOINT sp1;
SELECT pgv_set('pack', 'var_text', 'savepoint sp1'::text, true);
SELECT pgv_get('pack', 'var_text', NULL::text);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, ты прав. Вызов pgv_set() тут никакого смысла не несет.

pg_variables.c Outdated
pfree(DatumGetPointer(variable->value.scalar.value));
else if (get_actual_value_scalar(variable).typbyval == false &&
get_actual_value_scalar(variable).is_null == false)
pfree(DatumGetPointer(get_actual_value_scalar(variable).value));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я думаю, тут вместо вызова макроса три раза можно просто вызвать его один раз и присвоить результат переменной.


/* All records will be freed */
MemoryContextDelete(variable->value.record.hctx);
MemoryContextDelete(record->hctx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Возможно еще стоит его убрать из release_savepoint().

pg_variables.c Outdated
@@ -72,6 +75,11 @@ static HashVariableEntry *getVariableByNameWithType(HTAB *variables,
Oid typid,
bool create,
bool strict);
static HashVariableEntry *
getVariableByNameWithTypeAndTrans(HashPackageEntry *package, text *name,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется, что эту функцию нужно объединить с getVariableByNameWithType(), которая по сути была скопирована с некоторыми изменениями.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я давно ломаю голову над этими двумя функциями, но там не всё так просто: аргумент is_transactional известен не всегда. По сути, getVariableByNameWithTypeAndTrans() используется в функциях setter-ах (если их так можно назвать), а getVariableByNameWithType() - в getter-ах. В pgv_get() мы не знаем флаг is_transactional и он там и не нужен. По сути я могу вырезать строки из getVariableByNameWithType(), которые нужны для функции pgv_set(). Но там их не так уж и много.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется, что нужно не стремиться еще сильнее разделить эти функции, а объединить в одну, и флаг транзакционности проверять только при модификации.


if(variable->typid == RECORDOID)
{
insert_savepoint(variable, package->hctx);
Copy link
Contributor

@za-arthur za-arthur Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я не уверен, что правильно понял. Но получается, что при каждом SAVEPOINT копируются все записи record переменной? Нет ли более оптимального способа? Иначе это может быть довольно медленным, если записей очень много.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пока на ум приходит следующий способ. Я его не проверял и не уверен, что можно так делать. Это использование XID'ов в качестве ключа HashRecordKey, чтобы отличать записи разных транзакций.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Как решить эту задачу, я пока не знаю. Есть вариант сделать своего журнал изменений для таких переменных и хранить там операции, которые будут применены в commit. Но тут основной проблемой будет продумать, как работать с record-ами внутри подтранзакции, если в журнал мы записали, а в саму переменную изменения ещё не внесли.


if (!scalar->is_null)
{
scalar->value = datumCopy(
Copy link
Contributor

@za-arthur za-arthur Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут то же самое. Переменная еще не поменялась, а мы его уже копируем.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Переменную копируем до изменений, чтобы сами изменения вносить в новую версию точки сохранения. Можно было бы и не копировать старую версию, а просто сразу присваивать новое значение, но я использую эту функцию и для создания точки сохранения вне pgv_set(). В принципе, можно делать для каждой подтранзакции свой список изменённых переменных и работать уже с ним. Эта оптимизация имеет смысл только в случае большого количества подтранзакций и изменяемых переменных. Стоит делать?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно делать для каждой подтранзакции свой список изменённых переменных и работать уже с ним.

В итоге, сделал.

@funbringer

This comment has been minimized.

README.md Outdated
@@ -9,7 +9,7 @@
The **pg_variables** module provides functions to work with variables of various
types. Created variables live only in the current user session.

Note that the module does **not support transactions and savepoints**. For
Note that the module does **not support transactions and savepoints by default**. For
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я думаю, лучше все предложение целиком выкинуть. Транзакционность просто поддерживается, вот и все.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если "поддерживается, вот и всё", то она должна по-умолчанию работать. А тут не так.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не согласен.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Текущая формулировка привлекает внимание к проблеме, которой на самом деле нет.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хорошо, но мне кажется, что всё равно надо обозначить где-то вначале, что у переменных может быть разное поведение. Может сделать так:
Note that the module can either support and not support transactions and savepoints. For example:
Ну а дальше уже показать на примерах.

Copy link
Collaborator

@funbringer funbringer Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

module can either support and not support transactions

Как раз этот текст меня и смущает. Модуль в любом случае будет поддерживать транзакционность, это пользователь может хотеть или не хотеть использовать данную фичу. Поддержка зависит от кода, а не от настроек.

Я предлагаю написать примерно так:

By default, created variables are not transactional (i.e. they are not affected by begin, commit or rollback statements). This, however, is customizable via the ... options of pgv_set()...

Makefile Outdated
EXTVERSION = 1.1
DATA = pg_variables--1.0--1.1.sql
DATA_built = $(EXTENSION)--$(EXTVERSION).sql
$(EXTENSION)--$(EXTVERSION).sql: init.sql
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это правило нужно переместить в конец Makefile, потому что оно срабатывает по дефолту первым, и сборка ломается.

@codecov-io
Copy link

codecov-io commented Apr 20, 2018

Codecov Report

Merging #6 into master will increase coverage by 5.5%.
The diff coverage is 95.47%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master       #6     +/-   ##
=========================================
+ Coverage   89.52%   95.03%   +5.5%     
=========================================
  Files           2        4      +2     
  Lines         649      946    +297     
=========================================
+ Hits          581      899    +318     
+ Misses         68       47     -21
Impacted Files Coverage Δ
pg_variables_record.c 94.11% <100%> (+4.11%) ⬆️
pg_variables.c 95.1% <94.93%> (+5.67%) ⬆️
pg_bin/include/postgresql/server/lib/ilist.h 96.55% <0%> (ø)
pg_bin/include/postgresql/server/utils/palloc.h 100% <0%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e38097a...a3cefd3. Read the comment docs.

README.md Outdated
[![Build Status](https://travis-ci.org/postgrespro/pg_variables.svg?branch=master)](https://travis-ci.org/postgrespro/pg_variables)
[![codecov](https://codecov.io/gh/postgrespro/pg_variables/branch/master/graph/badge.svg)](https://codecov.io/gh/postgrespro/pg_variables)
[![GitHub license](https://img.shields.io/badge/license-PostgreSQL-blue.svg)](https://raw.githubusercontent.com/postgrespro/pg_variables/master/README.md)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CherkashinSergey , похоже ты случайно удалил нужные иконки.

@funbringer
Copy link
Collaborator

funbringer commented Apr 23, 2018

Результаты последнего ревью:

  1. Тестов чуть более, чем дофига, я очень мельком посмотрел. Наверняка их количество можно было бы оптимизировать.
  2. Код покрыт почти весь, это радует.
  3. Есть проблемы с форматированием, но я знаю верное решение, так что можно забить. Мы переформатируем весь проект после того, как этот pull request будет принят.
  4. Уже сказал в личке про memory context + MemoryContextAllocZero, жду коммита.

Я еще погоняю вручную, потому что так надежнее, но это будет завтра вечером. Жду ревью от @za-arthur (ему все-таки виднее).

В целом хорошо!

@za-arthur
Copy link
Contributor

Согласен. Я думаю, завтра смогу посмотреть. А так, видимо от копирования значений никуда не деться.

Copy link
Collaborator

@funbringer funbringer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Думаю, можно отдавать на тесты нашим DBA.

@za-arthur za-arthur merged commit aff8ac7 into postgrespro:master Apr 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants