Pull to refresh

Comments 57

Как в этом сценарии работает авторизация и проверка полномочий внешнего пользователя, обращающегося с HTTP-запросом к БД? Тоже средствами СУБД?

Благодарю за комментарий. Да, средствами СУБД. Создается пользователь PostgreSQL, в данном примере это http и клиентское приложение подключается к СУБД от его имени.

Если речь идет о логическом (web) пользователе, то через HTTP-заголовок Authorization, доступ к нему имеется. При входящем запросе внешний пользователь не имеет как такового доступа к данным. Вы сами определяете кому и какие данные будут в итоге возвращены, как и механизм авторизации, будь то пара login/password, API key или JWT-токен.

По духу похоже на встроенный вебсервер в TarantoolDB.

Может быть перспективно для увеличения быстродействия.

Спасибо!

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

Цитата с их сайта:

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

Лучше и не скажешь. Только вместо Lua PL/pgSQL.

Так делать не нельзя, так делать неудобно. О, смотрите, есть библиотека, которая приложение на Python/Java/Go/.NET превращает в БД, зачем нам теперь СУБД? :) Я помню времена приложений, построенных на СУБД, и это были времена тёмные и беспощадные. Очередной виток, с разработкой приложений и логики в БД, к которому возвращаться бы не хотелось. До сих пор из легаси систем выковыриваем логику из БД в приложения. Да, оно работает, да оно работает быстро, но на этом преимущества заканчиваются, начинаются проблемы. Понимаю, вопрос до сих пор спорный и дискуссионный, но хороший инструмент должен решать одну задачу и решать её максимально хорошо. Комбайны, не наш путь.

О! Я ждал этого «Так делать нельзя», бизнес логику в БД хранит нельзя :).

Казалось бы задумка простая: HTTP сервер (который на node.js можно реализовать в пару строк кода) принимает запрос и вызывает get или post функцию в СУБД для дальнейшей обработки. Звучит дико, согласен :). Тогда ради чего всё это?

Вся проблема, тех систем где пришли к выводу, что так делать нельзя в способе взаимодействия с СУБД.

Попытаюсь объяснить на примере кода: https://github.com/ufocomp/libdelphi/blob/425e39472f76e6a3602d1f61adc955b4fd6f1a34/src/Sockets.cpp#L4422C54-L4422C54 — это основной цикл обработки событий (event loop). В этом месте операционная система передаёт управление программе в тот момент когда данные поступили на Socket. В том или ином виде он (цикл) есть у всех приложений, в том числе и упомянутых Вами Python/Java/Go/.NET, но все эти приложения разработаны таким образом, что они изолированны от СУБД.

На низком уровне взаимодействие с PostgreSQL происходит тоже через Socket. Так вот у нас есть сокеты по которым поступают HTTP-запросы и у нас есть сокеты от СУБД и для того, что бы достичь максимальной производительности от связки HTTP-сервера с базой данных нужно эти сокеты поместить в один и тот же цикл обработки событий. Их нужно превратить в единую связку, так как это реализовано в Апостол и реализовано специально.

Поступивший HTTP запрос уже при выходе из цикла обработки событий будет находится в PostgreSQL. Останется только его обработать. Можно было бы обработать и на C++ но на PL/pgSQL скриптовей :)

В той четвёрке, что Вы упомянули, добавим туда ещё и node,js так делать не будут, потому, что там это не нужно. И поэтому их производительность, при работе с PostgreSQL будет ниже.

Я и не говорил, что нельзя. Но о какой такой "максимальной производительности" вы рассказываете, если у вас HTTP в связке? И сколько же вы там выигрываете, если отправим запрос напрямую в БД? При чём тут цикл обработки событий, если операции с БД являются i/o операциями?

Реальные приложения несколько сложнее, чем тупо весь запрос с потрохами отправить в БД, а результат с потрохами вернуть юзеру. При чём намного сложнее. Начиная от циклов разработки, поддержки, до наблюдаемости, отказоустойчивости, масштабируемости и безопасности.

Ну или речь идёт о каких-то совсем примитивных вещах, весьма далёких от реальных задач.

Когда инструмент начинают применять не по назначению, на выходе получается сразу жуткое легаси. Проведя сотни собеседований я часто сталкивался с вопросами "надеюсь у вас нет логики в БД? потому что если, есть нам с вами не по пути", и я их прекрасно понимаю. Так как имею и имел дело с логикой в БД.

Действительно бывают случаи, когда хранимка может серьёзно решить узкое горлышко. Но такие случаи редки, а при близком и внимательном рассмотрении, оказывается, что небольшой рефакторинг устраняет потребность размещения логики в БД. Сама такая возможность является ценным помощником и затычкой. Но целенаправленно пихать логику в БД, такое может прийти в голову только очень большому любителю и фанату БД :) Ни один ДБА с кем я общался, не видит в этом никаких проблем :)))

Хранить код только в БД и вести там разработку - да так делать нельзя! Но мы так и не делаем. Об этом я уже написал тут: https://habr.com/ru/articles/767442/#comment_26058754.

При правильной организации процесса разработки хранимая в БД процедура не является головной болью как и реализуемая ею бизнес логика.

Ответить на Ваши вопросы коротко не получится. Придётся обсудить очень много базовых вещей, объяснить, как это работает на низком уровне.

Я лишь хочу сказать, что если, при разработке информационной системы, использование PostgeSQL неизбежно, то куда эффективнее будет задействовать встроенный в саму СУБД инструмент разработки. Правда придется решить ряд технических вопросов. Обычно эти вопросы закрываются с помощью скриптовых языков программирования. Но меня такое решение не устроило.

Каким бы сложным не было Ваше приложение в его основе лежат базовые вещи - протоколы, технологии, стандарты и чем ближе вы к базовым вещам тем меньше задаетесь вопросом: "Какого черта оно не работает".

По поводу неудобно.

Вы пишите код на Python, код храниться в файле. Далее помещаете файл в контейнер в котором запускается бинарный файл python и выполняет код из файла. При выполнении кода с операционной системой взаимодействует процесс python.

Я пишу код на PL/pgSQL, он храниться в файле, далее скриптами «проливаю» (помещаю) этот код в PostgreSQL т. е. в БД (своего рода контейнер), а БД это что — правильно файл, т. е. код храниться тоже в файле, который выполняется бинарным файлом postgres. При выполнении кода с операционной системой взаимодействует процесс postgres.

Ну есть пару отличий, а так с точки зрения операционной системы всё одинаково.

Ну, на самом деле тут основной вопрос (на мой взгляд) в том, рационально ли писать свое приложение на таких языках, как PL/pgSQL. Потому что язык-то этот не самый лучший выбор на сегодня.


Ну то есть, плюсы, и достаточно очевидные, у такого подхода есть, быстродействие — потенциально один из них. Но и очевидные минусы тоже есть. Например, если вы таки хотите полноценный REST API наружу выставить, с какой-то сложной логикой обработки запросов, эта логика рано или поздно станет жрать ресурсы (потому что задача сложная, или потому что REST кто-то специально пытается завалить в диком интернете). Достаточно ли хороши средства планирования ресурсов постгреса, чтобы процессор, память и прочее таким обработчикам выделять, и запросы продолжать выполнять эффективно?

Согласен! Но для прототипов вокруг БД и внутренних админок самое то. Где проще "все в одном", а когда нужна масштабируемость - добавляются компоненты, разработчики и реализуется по канонам жанра...

Ну да. То есть, как нишевое решение — вполне.

Достаточно ли хороши средства планирования ресурсов постгреса, чтобы процессор, память и прочее таким обработчикам выделять, и запросы продолжать выполнять эффективно?

Достаточно. Ресурсы жрут процессы (со всеми вытекающими), а не код. Каждое подключение к PostgeSQL это отдельный процесс. Каждый запущенный скрипт на Python или node.js это отдельный процесс, плюс контейнер в котором он "живет". От процессов PostgeSQL избавится сложно, от остальных можно.

Вот полноценное RESTful API. Вот ресурсы сервера на котором всё это крутится:

Процессов много, памяти мало, но при этом её достаточно.

Достаточно.
Вообще говоря, ваших слов недостаточно.

Вот полноценное RESTful API.
Из этого вот пока совершенно непонятно, какую нагрузку оно там внутри создает. И какие ресурсы потребляет.

Как бы это попроще сформулировать… дело в том, что очень давно внутри Oracle DB можно было писать код на Java. Да и сегодня можно. И мы, было дело, писали. Иногда хочется, чтобы код внутри базы мог получить доступ к API и ресурсам, которые обычному PL/SQL недоступны. Или скажем запустить какой-то долгоживущий процесс (да хоть бы и HTTP-сервер, слушающий сокеты, или там message broker). Как я сразу и сказал, плюсы у такого решения есть. Только вот проблема в том, что оно приносит с собой и кучу проблем, из которых управление ресурсами лишь одна, не самая сложная. Просто обычная СУБД недостаточно хорошая платформа для выполнения произвольных приложений. Никто ни оракл ни постгрес для такой цели не проектировал, и многие вещи там делать неудобно.


Ну т.е. вашей идее — ей не сто лет в обед конечно, но у нас в проекте все это было году так в 2004. И сейчас, через почти 20 лет, я бы сначала сто раз подумал, и скорее всего так делать не стал бы. Что в общем совершенно не означает, что для вашего конкретного случая она вполне может оказаться неплохой.

Целью было не сделать так, что бы все запросы обрабатывались в БД, Целью было создать HTTP-сервер, который максимально быстро взаимодействует с PostgreSQL и на базе него создать сервер приложений.

Изначально планировалось взять за основу nginx, написать к нему модуль взаимодействия с PostgreSQL. Но по ходу изучения и кода nginx и документации к PostgeSQL возникла более интересная реализация. Повторятся не буду писал об этом выше отвечая на "Так делать не нельзя".

Когда Вы пишите приложение на языках большой пятёрки Python/Java/Go/.NET/node.js вы уже накладываете на себя ограничение в виде их библиотек, правил, инструментов. Да, бесспорно, в замен Вы получаете тоже не мало и скорость разработки ПО и стабильность в работе с памятью и кроссплатформенность. Но когда у вас есть возможность балансировать между кодом на C/C++ и PL/pgSQL то вопрос производительности остро не стоит. Не справляется PostgreSQL выносим в отдельный процесс (поток) пишем на С++. Благо архитектура Апостол позволяет это сделать легко и быстро.

А как apostol будет работать в кластерном варианте, совместно с расширением Citus, за балансировщиком http/s?

Если это расширение, то одно другому не должно мешать. На практике не сталкивался. Админы в любом случае не позволят Апостол смотреть портом наружу, поэтому как минимум nginx перед Апостол должен быть.

Да, расширение превращающее PostgreSQL в том числе в шардированную базу. Вам идея в копилку масштабируемости, проверить на нескольких узлах ;-)

Спасибо! Интересное решение, изучу повнимательнее. Я то по привычке изобретаю свои велосипеды.

Eсли с велосипедами и C++ вам в Яндекс устроиться, это может быть плюсом!

Citus не про репликацию совсем, а про шардирование и распределенные запросы по шардам(+ есть колоночное хранилище).

Ну т.е. вашей идее — ей не сто лет в обед конечно, но у нас в проекте
все это было году так в 2004. И сейчас, через почти 20 лет, я бы сначала
сто раз подумал, и скорее всего так делать не стал бы.

Точно! Классические признаки таких приложений - это отсутствие тестов всех видов и сложность с масштабированием, массовыми утечами данных в случае уявзвимостей, а также сложности с поддержкой и расширением функционала.

Если Notify почему-то не сработало, то как это обработать? Есть ли гарантия доставки хотя бы at least once?

NOTIFY — это реакция на добавление данных в таблицу. Если по каким-то причинам извещение не было доставлено (внешнее приложение остановлено), данные то остались. Когда внешнее приложение будет запущено оно отберет по статусу те записи которые следует обработать, обработает и сменит статус. Теряется крайне редко.

Тогда возникает резонный вопрос. Чем NOTIFY в данном случае выигрывает у REPLICATION SLOT?

Асинхронное уведомление, это не про репликацию. Это разные вещи.

Например Вам нужно отследить некие изменения в БД и отреагировать на эти изменения некими действиями. Вы подключаетесь к БД и в бесконечном цикле отслеживаете изменения в требуемой таблице т.е. закидываете БД не нужными SQL запросами.

Асинхронное уведомление позволяет от этого избавится. Сама СУБД известит ваше приложение о том, что произошли требуемые изменения.

REPLICATION SLOT и есть асинхронное уведомление средствами штатного расширения pgoutput.

Вы подключаетесь к БД и в бесконечном цикле отслеживаете изменения в требуемой таблице т.е. закидываете БД не нужными SQL запросами.

Вы явно не в теме. Почитайте, например, как этим пользуется Debezium https://debezium.io/documentation/reference/stable/connectors/postgresql.html

Принципиальная разница с NOTIFY в том, что через REPLICATION SLOT данные Вы точно не пропустите.

Я знаю, что NOTIFY и REPLICATION SLOT - разные вещи. Вы сами пишете:

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

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

Поэтому и вопрос. Зачем изобретать велосипед через NOTIFY, если REPLICATION SLOT без дополнительных усилий, автоматически обеспечит доставку внешнему приложению любого "запроса", как только он будут вставлен в таблицу, подключенную к REPLICATION SLOT?

Если еще раз на пальцах:

PL/pgSQL функция http.fetch является оберткой для функции http.create_request, в которой происходит запись в таблицу http.request с последующим вызовом команды NOTIFY через триггер добавления новой записи.

Вместо триггера на таблицу http.request, достаточно было включить на ней логическую репликацию, добавить ее в нужны слот репликации и штатными средствами получать оттуда все новые записи в этой таблице. Причем с ГАРАНТИРОВАННОЙ доставкой, вне зависимости от перебоев в работоспособности сервиса.

Мне не нужно передавать данные, мне нужно передать сигнал от одного процесса другому. Известить Апостол о том, что нужно отправить HTTP-запрос. Сам Апостол подключен к PostgeSQL через libpq.

Приложения, использующие libpq, отправляют серверу команды LISTENUNLISTEN и NOTIFY как обычные SQL-команды. Поступление сообщений от команды NOTIFY можно впоследствии отследить с помощью функции PQnotifies.

Я использую то, что есть в этой библиотеке: https://postgrespro.ru/docs/postgresql/16/libpq-async

// Так, мне неудобно и не нужно...
select * from pg_replication_slots;

И мне не нужна гарантированная доставка, если сигнал не будет доставлен то ни чего страшного не произойдет.

NOTIFY - тоже штатный механизм включенный в библиотеку libpq.

Мне не нужно передавать данные, мне нужно передать сигнал от одного процесса другому.

То есть сам запрос Вы не передаете? То есть это ложь:

PL/pgSQL функция http.fetch является оберткой для функции http.create_request, в которой происходит запись в таблицу http.request

Ничего в таблицу не пишется?

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

NOTIFY - тоже штатный механизм включенный в библиотеку libpq.

Но с той принципиальной разницей, что он не гарантируется доставку сигнала клиенту. А REPLICATION SLOT - гарантирует.

Всё происходит так как описано.

то сам факт записи в таблицу уже является сигналом от одного процесса другому.

Это как? Каким образом внешний процесс будет извещён о том, что в таблицу добавлена запись? Непрерывно сканируя её, закидывая БД ненужными запросами? Кажется мы уже это обсуждали.

Задача передать управление внешнему процессу, для этих целей NOTIFY и создан.

Может так будет понятнее.

В любом случае за комментарий спасибо, почитать про REPLICATION SLOT было полезно :)

Каким образом внешний процесс будет извещён о том, что в таблицу добавлена запись?

Если напрямую внутри собственного кода, то вызовом PostgreSQL блокирующей функции pg_logical_slot_get_changes() или pg_logical_slot_get_binary_changes(), которые не вернут управление до тех пор, пока в WAL не появится хотя бы одна новая подходящая запись.

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

Непрерывно сканируя её, закидывая БД ненужными запросами? Кажется мы уже это обсуждали.

И я Вам даже дал ссылку, которую Вы явно не открывали. И где написано, что никаких сканирований и ненужных запросов при этом не происходит.

Задача передать управление внешнему процессу, для этих целей NOTIFY и сознан.

Судя по статье, задача передать асинхронно данные внешнему сервису, а не только управление. И Вы как раз грузите БД, запросами по таблице http.request. Хотя эти запросы не нужны, так как все новые записи и без того пишутся в WAL. Достаточно их просто оттуда считать через REPLICATION SLOT.

Судя по статье, задача передать асинхронно данные внешнему сервису, а не только управление. 

Задача выполнить HTTP-запрос по данным из http.request. Вместе с NOTIFY передается идентификатор записи в таблице http.request, далее происходит запрос данных по этому идентификатору и выполнение HTTP-запроса с сохранением результата в http.request. Всё.

И как раз благодаря NOTIFY я не атакую запросами БД в ожидании новой записи в http.request. Я просто жду уведомление и по факту его получения выполняю описанное выше.

Достаточно их просто оттуда считать через REPLICATION SLOT.

Зачем таки сложности? Всё, что нужно для выполнения задачи уже в http.request.

Сами себе противоречите.

происходит запрос данных по этому идентификатору

после чего

я не атакую запросами БД

То есть, Вы сначала посылаете NOTIFY через очередь СУБД, а затем еще посылаете запрос данных по идентификатору, полученному через NOTIFY. Вместо того, чтобы вообще без запроса к очередям или таблицам БД просто получить данные из WAL и их обработать.

Зачем таки сложности? Всё, что нужно для выполнения задачи уже в http.request.

Вот я и спрашиваю. Зачем такие сложности? Ведь можно просто посадить хоть сотню сервисов на сотню секций Кафки в k8s и пусть они эти запросы асинхронно обрабатывают, вообще не обращаясь БД. Лишь возвращая результат через другой топик и синк.

с сохранением результата в http.request

Тем более. Еще и VACUUM грузите, вместо того вставлять ответы в другую таблицу.

после чего

После того так внешний процесс получит извещение :) которое будет обработано на уровне libpq.

Хорошо, как приложение подключенное к СУБД через libpq "просто получит данные из WAL"?

которое будет обработано на уровне libpw

Сначала оно попадет в очередь сервера и будет там находится до фиксации транзакции. А только после фиксации будет рассылаться всем клиентам, подписанным на эту очередь. А потом Вы еще лезете в БД, отыскивая по индексу данные, читая их из таблицы, да еще и обновляя статус там. Что явно на порядок большая нагрузка на сервер, чем последовательное чтение WAL.

Хорошо, как приложение подключенное к СУБД через libpq "просто получит данные и WAL"?

Вы издеваетесь? Я Вам и ссылку давал и указывал функции блокирующего чтения, которые предоставляют данные из WAL без обращения к очередям или таблицам БД. Это если в лом поднимать соединение репликации и явно выполнять START_REPLICATION SLOT slot_name LOGICAL ...

Суть в том, что если взять Ваш проект на гитхабе, когда запросы идут на один и тот же сервер, то это делается вообще без кодирования, исключительно средствами Confluent. Таблица запросов -> Source connector к PostgreSQL -> топик запросов -> HTTP Sink -> топики ответов (успешный, ошибка и dlq) -> PostgreSQL Sink -> таблицы ответов. Всё. Только настройка.

Вызов:

SELECT * FROM pg_logical_slot_get_changes('test_slot_1', NULL, NULL, 'include-xids', '0');

Заблокирует намертво одно из соединений, что уже неприемлемо при асинхронной обработке данных.

Тот механизм который предоставляет библиотека libpq в среде C/C++ выглядит белее привлекательно. Так как работает на уровне кода, а не SQL запросов.

Если Вы хотите продолжить обсуждение то найдите меня в телеграм по тому же нику. Готов показать как это работает в отладчике C++.

Заблокирует намертво одно из соединений, что уже неприемлемо при асинхронной обработке данных.

А породить нить/процесс, который и будет заниматься только чтением данных религия не позволяет?

Тот механизм который предоставляет библиотека libpq в среде C/C++

Я Вам на пальцах объяснял, как это можно легко запросами сделать. Но если бы Вы соизволили почитать по ссылке, как это делается в продуктивных системах, то обнаружили бы, что в соединении репликации после выполнение команды START_REPLICATION, данные попрут через CopyData. То есть, по тому же потоку, что и при выполнении команды COPY.

Если Вы хотите продолжить обсуждение

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

Во первых читал, а чтиво там не на пять минут. Во вторых я не спорю и более того соглашусь, что REPLICATION SLOT более производительное решение. Но и более сложное в реализации: помимо отдельной нити нужно ещё решить вопрос с разбором данных. Текущая реализация уже на NOTIFY и она проще в применении.

REPLICATION SLOT, благодарю ещё раз за предоставленный материал, я возьму за основу при переработке своего модуля репликации, там это, то, что нужно.

более сложное в реализации

В рамках того, что выложено на гитхабе, все делается вообще без программирования, исключительно настройками. И чем это сложнее?

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

Не изобретайте велосипед. CDC connect их сам разберет и разложит по нужным топикам. Остается только или воспользоваться готовым HTTP Sink, или написать свой Sink для более специфичных задач.

Можно? Можно!

Интересно? Интересно!

Стоит ли так делать? Не стоит

Не стоит хранить код в БД как единственную копию и вести разработку непосредственно в БД. Тут, да и я скажу, что так делать нельзя.

Сейчас, у нас по крайней мере, процесс разработки на PL/pgSQL особо ни чем не отличается от JavaScript.

Весь код хранится в репозиторий, при фиксации изменений отрабатывают механизмы CI/CD. Отрабатывают скрипты и код обновляется в БД (во всех копиях) с полной заменой всех функций. Да есть особенности в виде написания патчей для удаления старых функций или изменений структур таблиц, но это SQL что поделаешь.

Сам код находится в отдельной от таблиц (данных) схеме и если уж так получилось, что кто-то полез туда руками и код в БД стал не соответствовать тому, что в исходных файлах, то смело можно удалить всю схему в которой хранится только код и перезалить скриптами по новой.

Вопрос же не в можно или нельзя, а в том кому как удобно и кто как знает, умеет, может :)

Если возможность посылать HTTP запросы из кода на pl/pgsql еще понятна (сам таким балуюсь, когда необходима синхронность в получении результатов из REST/gRCP сервисов), то нагружать PostgreSQL веб-серверной логикой - выглядит недальновидно, с точки зрения масштабирования. Все же сервисы в k8s масштабируются легко и просто, тогда как масштабирование PostgreSQL - тяжело и сложно.

P.S. Когда синхронность не нужна, то коммуникацию между PostgreSQL и сервисами в обе стороны выполняет Kafka

Вы по любому нагружаете PostgreSQL запросами из вне, а если бизнес логика тесно связана с этими данными, то отработка кода бизнес логики внутри СУБД будет эффективнее, чем таскать данные по сети между разными процессами и отрабатывать логику за пределами СУБД.

Сделать контейнер на подобии того, что в этой статье задача не сложная. Развернуть несколько таких контейнеров (с одинаковым кодом) тоже не проблема. Проблема обеспечить идентичность данных в разных базах данных. Но в моём случае она решаема.

Вы по любому нагружаете PostgreSQL запросами из вне

Естественно нагружаю. У меня тоже много бизнес-логики на СУБД. Но в сервисах можно кешировать результаты, например в Redis, существенно снижая нагрузку на СУБД. И это не считая снятия с СУБД нагрузки на десериализацию/сериализацию, включая конвертации чисел из десятичного представления в двоичное и обратно.

Например, кешируя в Redis всего лишь справочники, удалось снизить загрузку на СУБД в несколько раз. Например, там, где раньше в запросе сервер возвращал "Код станции", "Наименование станции", "Код участка", "Наименование участка", "Код отделения", "Наименование отделения", "Код дороги", "Наименование дороги" и т.п., теперь достаточно вернуть ID станции, а все остальные атрибуты будут взяты из Redis, поддерживаемой в актуальном состоянии из топиков Кафки. Я думаю видно, что с СУБД были сняты еще и четыре JOIN с справочниками (Станции, Участки, Отделения, Дороги). Для ряда запросов, например по дислокации, это уже два десятка JOIN!

Проблема обеспечить идентичность данных в разных базах данных. Но в моём случае она решаема.

До тех пор, пока мастер справляется. И это очень серьезное ограничение.

Это всё верно, если мы исходим из ситуации когда кэширование становится необходимым. А если в этом нет необходимости, если запросы и так отрабатываются достаточно быстро:

API Log
API Log

В любом случае одно другому не мешает. Можно кэшировать запросы на уровне HTTP-сервера, на этом же уровне можно задействовать и Redis. Правда это уже будет код на C++ :).

До тех пор, пока мастер справляется. И это очень серьезное ограничение.

:) так в том-то и дело, что можно каждый узел сделать мастером.

Давайте не будем погружаться в проблемы мульти-мастер репликации. Это огромная и сложная тема. Может на досуге почитать и тогда поймете, почему мульти-мастер репликация не часто используется, да и то, преимущественно, для multi-tenent. Когда, например, в БД уже все транзакционные данные разделены на разные tenants. Например, компании (юр. лица).

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

если запросы и так отрабатываются достаточно быстро

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

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

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

Краткий текст статьи (после GPT):

Рассматривается способ отправки и обработки HTTP-запросов непосредственно из базы данных PostgreSQL.
Автор предлагает использовать функции http.fetch, http.get и http.post для отправки запросов, а также функции обратного вызова для обработки полученных данных.
Он также показывает, как можно настроить HTTP-сервер в PostgreSQL для обработки входящих запросов.
Для демонстрации примеров автор использует инструмент pgweb, который позволяет выполнять SQL-запросы и просматривать содержимое таблиц и функций на PL/pgSQL. В статье также представлен пример использования асинхронных уведомлений PostgreSQL для взаимодействия с клиентским приложением, которое выполняет HTTP-запросы и сохраняет результаты в базе данных.
Автор приводит практический пример использования этого механизма для автоматического списания денег с карты клиента.
В целом, статья предлагает простой и гибкий способ взаимодействия с внешними системами из PostgreSQL.

Краткий ответ на все вопросы - "с трудом".

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

Классический троллейбус-из-буханки - на вид как настоящий и даже наверное работает - но решительно непонятно "зачем"?

- отладка проблемных запросов - как траблушить проблемы?

Через журналы событий. Через внутренние и внешние инструменты анализа производительности PostgeSQL.

- тяжелые запросы, потребляющие проц и память - как будет работать обработчик, когда на сервере тяжелая нагрузка?

Это очень обобщенный вопрос который можно адресовать любому программному продукту. Есть сервера на которых происходит разработка и тестирование, в том числе и нагрузочное есть определённое понимание пределов железа и системы в целом.

- балансировка нагрузки - в случае роста нагрузки, как масштабироваться горизонтально, если данные и обработка в одной точке и неделима?

Так у вас же не один процесс запущен и не только он занимается обработкой входящих данных: https://habr.com/ru/articles/767442/#comment_26058162

В настройках Апостол можно указать как количество рабочих процессов (аналогично nginx), так и пул подключений к СУБД. Если железо позволяет можно одновременно держать сотни готовых подключений. Обработка запросов асинхронная через epoll поэтому запас прочности большой, но да, всему есть предел. На самом деле вопрос отрытый :)

Например ETL с подгрузкой и обработкой больших объемов данных.

Если объем данных большой и есть четкое понимание, что пихать это всё в одну ячейку таблицы, с последующей обработкой на PL/pgSQL не самая удачная затея. То на помощь приходит C++. Апостол это не только про PostgreSQL- это фреймворк для разработки серверного программного обеспечения. Можно создать отдельный модуль или процесс который будет заниматься разбором данных.

- командная разработка - как реализовать стандартный для команд SDLC?

Да всё как и у всех. Упоминал об этом тут: https://habr.com/ru/articles/767442/#comment_26059064

- SLA - как гарантировать время ответа, если бешенный запрос может нагрузить сервер по самые тапочки?

Бывает, может "призадуматься" один из процессов, но как я уже упомянул их не мало, да и это уже повод задуматься, что не так.

На синтетических тестах грузили так, что количество запросов превышало в разы количество клиентов (которые вряд ли ринутся все разом в одну секунду) и результат заказчика устроил.

Вопросы по реализации и использованию:

Реализация на примере.

Один из текущий проектов предполагает активное взаимодействие с ИИ. Это не только ChatGPT, много сервисов для работы с изображениями.

Midjourney один из них. Но так как было четкое понимание, что из-за проблем с коммерческой эксплуатацией этот сервис нам не подходит, создали временное решение на node.js, не входящее в основную сборку. На выходе получится микросервис со своим не большим API. Взаимодействие с ним происходило так же как описано в этой статье. Обратная связь через webhook.

Hidden text

Со временем перешли на RunPod, а с него на Replicate.

Я к тому, что ни кто не отменяет микросервисную архитектуру, а наоборот. При этом СУБД как источник данных превращается в центр управления, дережируюя микросервисами.

Не хотите, по возможности, добавить в статью список с кратким сравнением достоинств и ограничений вашего продукта и Hasura (наиболее известного проекта со схожей функциональностью)?

Благодарю! Не сталкивался, почитаю.

Sign up to leave a comment.

Articles