Search
Write a publication
Pull to refresh
7
0
Send message

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

Самая большая проблема здесь, как вы сами отметили, неравномерное распределение ключа. Даже единичный случайный выброс (2,1,5,10000) ломает всю картину.

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

Да. Ключевые слова: плагин Remotely Save + Webdav + Облако. Так как Хабр не очень любит ссылки в комментах, скинул ссылку на подробную инструкцию в личку.

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

Кажется, что ничего из перечисленного не подпадает под определение "более-менее современная версия EF одно выражение Where трансформирует во что-то кроме SELECT … FROM … WHERE … ". Но это, в любом случае, думаю, не принципиально.

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

В любом случае, инструмент есть, а пользоваться им, или нет, уже пусть читатели сами решают.

А можно, пожалуйста, практический пример того, как более-менее современная версия EF одно выражение Where трансформирует во что-то кроме SELECT … FROM … WHERE …? Спрашиваю без малейшего стёба, так как я такой риск сознательно откидывал как несущественный.

Я прекрасно понимаю, как можно «положить» этот запрос, но честно смог придумать только четыре сценария:

  1. Применение ForUpdate к сложносоставному запросу, к которому в принципе нельзя применять эту команду на уровне SQL. Ну как бы сам себе злобный Буратино, не надо отвёрткой гвозди забивать.

  2. В будущих версиях EF решат сломать обратную совместимость при генерации запросов и, например, начать явно прописывать ; в конце. Ок, это будет не первый на моей памяти случай, когда внешняя библиотека что-то ломает. Заранее это не предскажешь, так как могут и две ; подряд поставить, например. Тут, главное, при подъёме версий библиотек автотесты не забывать гонять.

  3. Смена базы данных, при которой надо вместо FOR UPDATE начинать писать совсем другой код и по другим правилам. Тут тоже заранее хз, что делать, так как смена базы дело достаточно капитальное и не зная целевую базу сложно универсальный код написать. Тем более, что тут любой raw sql или вызов хранимую отломится точно также.

  4. Другой интерсептор, написанный без учёта совместимости с уже имеющимися. Например, добавляющий в конец SELECT запроса ; по принципу «а для красоты». Такое только на ревью отслеживать, плюс, опять же, автотесты.

Применять FOR UPDATE на какой-нибудь сложный кросс-джойн по нескольким таблицам с агрегаторами и группировкой - не надо так. Он там просто не сработает, причём EF там будет ни при чём. Ну а что нагенерирует EF на более-менее типовой селект по одной таблице с WHERE в качестве условия - вариантов там не особо много.

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

Да, но лучше, думаю, просто Ordinal в этом случае использовать, так как смысла нет регистро-независимое сравнение делать. Тут жёстко прописанная константа, которую мы вряд ли просто так менять будем, а на проверку вариантов написания букв в разных регистрах тоже время тратится.

Зависит от. Понятно, что если структура базы такая, что нагенерированный EF код надо постоянно перепроверять, то ну его нафиг. Тут, да, никакие интерсепторы не помогут - только RawSql, а если кривых запросов ещё и достаточно солидный процент от общего числа, то стоит в принципе подумать на тему отказа от EF.

Но сделать интерсептор для того же For Update вместо переписывания в 3-х местах кода на нативный SQL - вполне себе годное решение, как мне кажется. И да, я полностью согласен, что хранимка в данном случае работала бы немного быстрее. Нюанс в том, что при ожидаемой частоте апдейтов потратили бы больше времени разработчика на переписывание, чем сэкономили бы машинного времени при такой оптимизации.

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

Дисплей на тенях - вряд ли, хотя всё возможно. А вот часть логической операции «исключающее или» для чисто оптических компьютеров - сделано. Понятно, что от этого до практического применения ещё очень далеко, да и цена такого «процессора» космическая, но тенденция интересная.

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

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

Либо просто запрос с длинным временем выполнения.

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

Ну, либо если такой сценарий реально зачем-то нужен на проде и есть описанные риски, то флаг IsTraceEnabled ставим в false и для этого запроса работает старая логика логирования.

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

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

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

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

Для понимания: все записи метками помечаются в обязательном порядке, логи живут на диске две недели. Но при нагрузке в 1000+ rps размер суточного файла в 50+ Gb на одной реплике был ежедневной реальностью, а при пиковой нагрузке до 140 Gb доходило. Сейчас снизилось до 20 Gb без полной переборки всех мест, где пишутся логи и определения степени их критичности (что тоже отдельная задача с далеко не нулевой трудоёмкостью). При этом за прошедшие 2 месяца не было ни одной ситуации, когда понадобились бы логи, которые сейчас, по факту, идут в сброс после окончания запроса, а вот "простынёй" пользоваться приходилось. Субъективно поудобнее стало.

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

Не совсем. Там Warn и выше пишутся И сразу в лог И в трассировку, которая потом будет показана единой простынёй. Иными словами, мы видим сообщения уровня Warn+:

  1. В моменте и в том месте, где они произошли, Это позволяет отследить событие относительно других запросов/логов уровня warn+

  2. В единой "простыне" сообщений запроса. Там сообщение показывается на своём месте относительно других сообщений этого же запроса без чехарды с записями других запросов. Это сообщение записывается в конце выполнения запроса и, да, проследить info относительно info в других запросах (особенно те, которые не запишутся), нельзя

Нет, если надо сохранить данные нескольких отдельных запросов, то этот подход не подходит. Он работает в рамках отдельно взятого запроса. Я уже писал ниже, что применял его для API без хранения каких-либо промежуточных состояний.

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

Спасибо за мысли! У нас эти советы не все применимы из-за специфики (stateless, api), но похожую идею, только, наоборот, с выключением логов для конкретного пользователя тоже реализовывали. Были случаи, когда клиенты начинали упорно долбить запросами при закончившемся ключе, организовывая мини-DDOS с "белых" IP, и надо было их либо вручную под протоколы обработки DDOS подводить, хотя всё стабильно работало, либо минимизировать число записей в логах.

Information

Rating
Does not participate
Works in
Registered
Activity