валют начиная с заданной даты. Эта функция очень часто вызывается в более крупной функции расчётов, и на время работы этой крупной функции, можно считать, что данные курсов не меняются, т.е. можно возвращать один и тот же результат. Вот насколько будет корректным сделать функцию возвращающую курсы валют stable? Не очень понял из документации в какой момент сбрасываются данные этого "кэширования". Т.е. при каждом вызове основной функции, каждом начале сессии, или никогда?
https://postgrespro.ru/docs/postgrespro/9.5/xfunc-volatility У функций, написанных на SQL или на любом другом стандартном процедурном языке, есть ещё одно важное свойство, определяемое характеристикой изменчивости, а именно видимость изменений, произведённых командой SQL, которая вызывает эту функцию. Функция VOLATILE будет видеть такие изменения, тогда как STABLE и IMMUTABLE — нет. Это поведение реализуется посредством снимков в MVCC (см. Главу 13): STABLE и IMMUTABLE используют снимок, полученный в начале вызывающего запроса, тогда как функции VOLATILE получают свежий снимок в начале каждого запроса, который они выполняют. Вследствие такой организации работы со снимками, функцию, содержащую только команды SELECT, можно безопасно пометить как STABLE, даже если она выбирает данные из таблиц, которые могут быть изменены параллельными запросами. Postgres Pro выполнит все команды в функции STABLE со снимком, полученным для вызывающего запроса, так что они будут видеть одно представление базы данных на протяжении всего запроса. ... Но думаю в случае с курсами валют, Вы вряд ли ощутите разницу.
Спасибо, это я читал, но вот там как раз сказано, что снимок делается в начале "вызывающего запроса" не очень для меня понятная фраза. Т.е. вызвали функцию А, она вызвала Б (которая STABLE). Сделали снимок... в той же сессии дернули ещё раз А, она Б... снимок будет тот же использоваться или снова будет делаться снимок? А если новая сессия, то снимок уже новый?
Давайте протестим... Запускаем скрипт: 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 и повторяем процедуру. Что получаем?
Вернусь к компьютеру, попробую. Думал, что кто-то опытом поделится, чтобы на тест время не тратить т.к. гугл (а прочитал я прилично там) ответа не дал. Дальше была надежда на быструю помощь сообщества, если кто-то уже сталкивался, далее тест писать и проверять.
Что здесь пробовать? 5 мин на все про все. Ответа от "опытных" гораздо дольше ждать. Кратко: stable - отличный способ выстрелить себе в ногу.
Хмм... со stable получаем правильный результат, с volatile — "it depends" (от уровня изоляции транзакции). > Кратко: stable - отличный способ выстрелить себе в ногу. Эээ... наоборот же, нет?
Мне кажется, что там всё очень понятно написано... Снимки делаются в зависимости от уровня изоляции транзакции — для Read Committed в начале каждого запроса, для Repeatable Read и Serializable — при первом читающем/пишущем запросе в транзакции. Но: "функции VOLATILE получают свежий снимок в начале каждого запроса, который они выполняют." Т.е. в том числе могут выполнять (если в них есть запросы) дополнительные снимки при выполнении одного и того же запроса (если вызываются в нём несколько раз, или в нём несколько volatile функций). Поэтому они "видят" больше, чем должны бы, в некоторых ситуациях. К примеру, volatile function, вызываемая во время UPDATE, будет видеть его частичные результаты (а не состояние на начало запроса, как STABLE).
Т.е. ответ на этот вопрос — да, сделать такую функцию STABLE будет корректно.
»Эээ... наоборот же, нет? Это смотря с какой стороны смотреть))
Я в норме ожидаю в одном запросе видеть все считываемые из базы данные в одном и том же состоянии. Volatile functions могут это нарушать, т.е. приводить к аномалиям прямо "внутри" одного запроса. Это и есть "выстрелить себе в ногу", IMHO.
Дефолтное значение - 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 прямо в одном запросе, чего не должно быть в норме.
Большое спасибо. Дело не в 5 минутах, а то что провести тест раньше вечера нет возможности, а понимание как оно работает было нужно по возможности как можно раньше. А так да, с Вами абсолютно согласен, тест сделать быстрее в данном случае.
Большое спасибо за развёрнутый ответ. Стало понятнее. Про разную изоляцию сразу не подумал, увы :(
Обсуждают сегодня