172 похожих чатов

Подскажите, по STABLE для функций. Есть функция, которая возвращает ставки

валют начиная с заданной даты. Эта функция очень часто вызывается в более крупной функции расчётов, и на время работы этой крупной функции, можно считать, что данные курсов не меняются, т.е. можно возвращать один и тот же результат. Вот насколько будет корректным сделать функцию возвращающую курсы валют stable? Не очень понял из документации в какой момент сбрасываются данные этого "кэширования". Т.е. при каждом вызове основной функции, каждом начале сессии, или никогда?

14 ответов

24 просмотра

https://postgrespro.ru/docs/postgrespro/9.5/xfunc-volatility У функций, написанных на SQL или на любом другом стандартном процедурном языке, есть ещё одно важное свойство, определяемое характеристикой изменчивости, а именно видимость изменений, произведённых командой SQL, которая вызывает эту функцию. Функция VOLATILE будет видеть такие изменения, тогда как STABLE и IMMUTABLE — нет. Это поведение реализуется посредством снимков в MVCC (см. Главу 13): STABLE и IMMUTABLE используют снимок, полученный в начале вызывающего запроса, тогда как функции VOLATILE получают свежий снимок в начале каждого запроса, который они выполняют. Вследствие такой организации работы со снимками, функцию, содержащую только команды SELECT, можно безопасно пометить как STABLE, даже если она выбирает данные из таблиц, которые могут быть изменены параллельными запросами. Postgres Pro выполнит все команды в функции STABLE со снимком, полученным для вызывающего запроса, так что они будут видеть одно представление базы данных на протяжении всего запроса. ... Но думаю в случае с курсами валют, Вы вряд ли ощутите разницу.

Dmitriy SH.-Shubin Автор вопроса
𒀭
https://postgrespro.ru/docs/postgrespro/9.5/xfunc-...

Спасибо, это я читал, но вот там как раз сказано, что снимок делается в начале "вызывающего запроса" не очень для меня понятная фраза. Т.е. вызвали функцию А, она вызвала Б (которая STABLE). Сделали снимок... в той же сессии дернули ещё раз А, она Б... снимок будет тот же использоваться или снова будет делаться снимок? А если новая сессия, то снимок уже новый?

Dmitriy SH. Shubin
Спасибо, это я читал, но вот там как раз сказано, ...

Давайте протестим... Запускаем скрипт: drop table if exists rates; create table rates ( currency_id int primary key, rate numeric, updated timestamp default clock_timestamp() ); insert into rates(currency_id, rate) values (1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100); create or replace function get_rates() returns table (currency_id int, rate numeric, updated timestamp) language plpgsql stable as $$ begin for i in 1 .. 11 loop raise notice '%', 11 - i; perform pg_sleep(1); end loop; return query select r.currency_id, r.rate, r.updated from rates r; end; $$; select get_rates(); Пока счетчик тикает, открываем еще один psql и вбиваем: update rates set rate = 100500, updated = clock_timestamp() where currency_id = 3; Смотрим на результат. Затем убираем stable и повторяем процедуру. Что получаем?

Dmitriy SH.-Shubin Автор вопроса
𒀭
Давайте протестим... Запускаем скрипт: drop table...

Вернусь к компьютеру, попробую. Думал, что кто-то опытом поделится, чтобы на тест время не тратить т.к. гугл (а прочитал я прилично там) ответа не дал. Дальше была надежда на быструю помощь сообщества, если кто-то уже сталкивался, далее тест писать и проверять.

Dmitriy SH. Shubin
Вернусь к компьютеру, попробую. Думал, что кто-то ...

Что здесь пробовать? 5 мин на все про все. Ответа от "опытных" гораздо дольше ждать. Кратко: stable - отличный способ выстрелить себе в ногу.

𒀭
Давайте протестим... Запускаем скрипт: drop table...

Хмм... со stable получаем правильный результат, с volatile — "it depends" (от уровня изоляции транзакции). > Кратко: stable - отличный способ выстрелить себе в ногу. Эээ... наоборот же, нет?

Dmitriy SH. Shubin
Вернусь к компьютеру, попробую. Думал, что кто-то ...

Мне кажется, что там всё очень понятно написано... Снимки делаются в зависимости от уровня изоляции транзакции — для Read Committed в начале каждого запроса, для Repeatable Read и Serializable — при первом читающем/пишущем запросе в транзакции. Но: "функции VOLATILE получают свежий снимок в начале каждого запроса, который они выполняют." Т.е. в том числе могут выполнять (если в них есть запросы) дополнительные снимки при выполнении одного и того же запроса (если вызываются в нём несколько раз, или в нём несколько volatile функций). Поэтому они "видят" больше, чем должны бы, в некоторых ситуациях. К примеру, volatile function, вызываемая во время UPDATE, будет видеть его частичные результаты (а не состояние на начало запроса, как STABLE).

Т.е. ответ на этот вопрос — да, сделать такую функцию STABLE будет корректно.

Yaroslav Schekin
Хмм... со stable получаем правильный результат, с ...

»Эээ... наоборот же, нет? Это смотря с какой стороны смотреть))

𒀭
»Эээ... наоборот же, нет? Это смотря с какой сторо...

Я в норме ожидаю в одном запросе видеть все считываемые из базы данные в одном и том же состоянии. Volatile functions могут это нарушать, т.е. приводить к аномалиям прямо "внутри" одного запроса. Это и есть "выстрелить себе в ногу", IMHO.

Yaroslav Schekin
Я в норме ожидаю в одном запросе видеть все считыв...

Дефолтное значение - VOLATILE. Дядьки из оракла так же придерживаются этой концепции, так что...

𒀭
Дефолтное значение - VOLATILE. Дядьки из оракла та...

Потому что проблему останова в PostgreSQL тоже не решили. ;) Т.е. статически доказать, что у функции есть гарантии stable или immutable, тупо невозможно. А если их "вслепую" прописывать (сделать default immutable, например), то функции, которые на самом деле volatile, будут сразу вести себя совершенно неверно. Поэтому прописывать volatility classification нужно правильно, да и всё. Я же имел в виду, что "A query using a volatile function will re-evaluate the function at every row where its value is needed" для функции, которая просто читает из БД — это non-repeatable read прямо в одном запросе, чего не должно быть в норме.

Dmitriy SH.-Shubin Автор вопроса
𒀭
Что здесь пробовать? 5 мин на все про все. Ответа ...

Большое спасибо. Дело не в 5 минутах, а то что провести тест раньше вечера нет возможности, а понимание как оно работает было нужно по возможности как можно раньше. А так да, с Вами абсолютно согласен, тест сделать быстрее в данном случае.

Dmitriy SH.-Shubin Автор вопроса
Yaroslav Schekin
Мне кажется, что там всё очень понятно написано......

Большое спасибо за развёрнутый ответ. Стало понятнее. Про разную изоляцию сразу не подумал, увы :(

Похожие вопросы

Обсуждают сегодня

30500 за редактор? )
Владимир
47
а через ESC-код ?
Alexey Kulakov
29
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
13
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Ребят в СИ можно реализовать ООП?
Николай
33
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
в JclConsole объявлено так: function CtrlHandler(CtrlType: DWORD): BOOL; stdcall; - где ваше объявление с stdcall? у вас на картинке нет stdcall
Karagy
8
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
program test; {$mode delphi} procedure proc(v: int32); overload; begin end; procedure proc(v: int64); overload; begin end; var x: uint64; begin proc(x); end. Уж не знаю...
notme
6
Карта сайта