There will be no singularity – Telegram
There will be no singularity
1.99K subscribers
248 photos
15 videos
5 files
995 links
Smartface, technologies and decay
@antonrevyako
Download Telegram
Процесс подготовки ребенка к школе - безумно интересное занятие.
В процессе объяснения открываешь для себя баги/фичи привычных вещей.

Например, средний род у числительных. 1 - имеет средний род, остальные цифры нет. Одно пальто - два пальто.
Зачем так? За что? Влияет ли средний род единицы на величие и могущество языка?..

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

Во французском, говорят, с числительными еще больше веселья...

Но ладно гуманитарные науки, там не с кого спросить.

Вот в твиттерах несколько дней горел срач двух фэндомов Коняев vs Брагилевский (математики vs программисты).
Первый (препод топологии на ММФ МГУ, издатель N+1) накатил на второго (препод ФМК в СПГУ, один из разработчиков компилятора haskell) на тему того, что языки программирования - это просто формальный набор инструкций из конечного набора инструкций. А математика - это не набор инструкций.

Мне как недоматематику и недопрограммисту наблюдать за этим было крайне интересно и познавательно.

Но у меня для вас плохие новости.

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

В моей молодости (кхе-кхе) задача бы была закенселена (хе-хе) с диагнозом "недостаточно данных".
Т.к. бот для постинга не приклеивает ссылку на нативные каменты, а в чате всего 30 участников, делаю отдельный пост для желающих поворчать в каментах :)

PS: обведено и подчеркнуто неверно...
Внезапно выяснилось, что дуров опять убрал стену и ссылка на чатик есть только в инфе о канале. Поэтому вот она, добавляетесь, побугуртим :)
There will be no singularity pinned «Внезапно выяснилось, что дуров опять убрал стену и ссылка на чатик есть только в инфе о канале. Поэтому вот она, добавляетесь, побугуртим :)»
Вот я все на ORM гоню.

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

Это какая-то лютая смесь ligualeo-style и mongodb:

select * from account_update($${
"phones":[{"phone_id":"4", "phone":"3123334557"}]}
$$::json, 4);

Описание с сайта конференции:

Хорошо известно, что, хотя производительность базы данных велика и каждый запрос выполняется за миллисекунды, общее время отклика приложения может быть медленным, поэтому пользователи могут долго ждать ответа. Мы знаем, что проблема не в базе данных, а в том, как разработчики приложений с ней общаются. В частности, речь идет об ORM - Object-Relational Mappers. Разработчики баз данных ненавидят их, но разработчики приложений любят их, потому что они позволяют разрабатывать приложения без каких-либо знаний о внутреннем устройстве СУБД. В результате производительность системы часто оказывается неприемлемо низкой.

Единственный способ изменить это - предоставить разработчикам приложений такой же простой в использовании инструмент, как ORM, но позволяющий избежать распространенных ошибок ORM. Вот почему мы разработали NORM - No-ORM Framework.

Во время этой презентации мы рассмотрим примеры кода из репозитория https://github.com/hettie-d/NORM и узнаем, как создавать «транспортные объекты» для эффективной передачи данных между приложениями и базами данных.


Нет, чтоб взять parsers.dev и все контракты нагенерить, будем вот такое изобретать!

У меня, конечно, нет CS PhD, как у авторов, но вот то, что на скрине, это какое-то форменное вредительство...

PS: кстати да, с помощью parsers.dev на основании SQL можно нагенерить контракты или весь код бэкенда на любом языке. Или вот такую тулзу зацените, сэкономит кучу времени (если вы, конечно, не пишете так, как на скрине)
Мне кажется, что драматически улучшить уже нельзя...

Если у вас много приватных реп, но в стату отчет по ним не попадает, можно это исправить в contribution settings -> private contribution.
Светите свои отчеты в каментах :)
На самом деле на скрине выше почти месяц без коммитов. МЕСЯЦ!
Как-то не замечаешь этого, когда прокрастинируешь.

Но я хотел обратить ваше внимание на другое. Если решите начинать свой проект, внимательно изучите этот скрин :)

Давайте посчитаем, сколько вы работаете в найме из 365 дней. В РФ на 2021:

календарных дней: 365
выходных/праздничных дней: 118
рабочих дней: 247

отпуск: 30 дней
болеем две недели весной: 15 дней
болеем две недели осенью: 15 дней

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

Когда у вас стартап:
Работаем 146% времени.
Получаем 0% денег.

Пользуясь случаем напоминаю:
https://twitter.com/year_progress/status/1368607386320179211
Я еще от новых достижений в джанге не отошел, как тут такое...

```
def foo(bar=[]):
bar.append(1)
return bar

print(foo()) # [1]
print(foo()) # [1, 1]
print(foo()) # [1, 1, 1]
```

И эти люди запрещают ковыряться мне в носу писать на типизированном js!

Еще вспомнилось
У нас в чате регулярно происходят академические срачи. Последний, например, про то как внутри питон работает. Присоединяйтесь! https://news.1rj.ru/str/joinchat/Tqy4Z0AKtdiFOz16
Если этот пост соберет 1000 лайков, сяду пилить свою облачную бд.
опен-кором, с движками по подписке и всяким таким....

https://baserow.io/

*предложение не является публичной офертой
На наступившей неделе мне доверили честь вести коллективный твиттер аккаунт https://twitter.com/sqaunderhood
Я расскажу чем мы занимаемся и как все устроено.
Сюда буду дублировать лонгридами в конце дня.
Постараюсь в своем твиттере и линкеде постить копию на английском (лет ми спик фром май харт ин иглиш).

Поэтому лайк, шер, и что там еще полагается
План на неделю в twitter.com/sqaunderhood:
- Категории проблем. Проблемы в схеме БД
- Проблемы в запросах
- Проблемы на границе сопряжения приложения и базы
- Создаем свой анализатор
- Как можно автоматизировать поиск проблем
- Для чего еще пригодится анализатор
- Забавные wtf в PostgreSQL и Snowflake

Дисклеймер: все примеры тестировались в PostgreSQL или SnowflakeDB. В других базах может быть иначе.
Каждый из озвученных тезисов не будет иметь ультимативный характер и приводится в справочных целях.
Используйте на свой страх и риск :)

Независимо от способа создания SQL (руками или при помощи кодогенерации), проблемы с запросами можно разделить на несколько больших групп:
1) naming convention
2) производительность
3) безопасность
4) архитектура


1) Naming convention
В некоторых архитектурах (data vault, anchor modelling) соглашение об именах является главной скрепой :)
Но и в обычной жизни это тоже пригодится. Особенно при работе с аналитическими базами.
В работе дата инженеров случаются смены вендеров БД.
Неожиданно можно узнать, что часть имен считаются ключевыми словами в одних базах и не считаются таковыми в других.
Например, LIMIT в SnowflakeDB (примеры в конце недели)
Но объективно, это не сильно аффектит качество ПО :)

2) Производительность
Кажется, что это самая главная проблема при работе с БД. Большая часть проектов пишется с использованием ORM.
Но как мы знаем, ORM придумывали не для того, чтобы у вас все быстро работало, а чтобы быстро работали вы :)

3) Безопасность
В сознании разработчика слова "безопасность" и "БД" находят пересечения только в SQL инъекциях. Эти проблемы давно решены на уровне фреймворков и специально об этом думать не надо, просто используй best practice. ORM или нет - не важно. Эскейпить строки умеют все.
Но это далеко не все. Есть еще, например, регуляторные требования относительно персональных или банковских данных. Нельзя допустить, чтобы кто-то из сотрудников имел доступ одновременно к имени и фамилии пользователя. Или чтобы система выводила полный номер карты.
А что, если запрос может привести к отказу в обслуживании из-за большого объема обрабатываемых данных? В треде https://twitter.com/SanSYS/status/1299657208934916097 грустные истории о том, как lingualeo не глядя сохраняли весь пользовательский инпут в JSON. И потом его вытягивали.
Да, это больше про производительность. Но часто проблемы будут иметь несколько сторон.

4) Архитектура
Самая богатая на проблемы группа. Количество разных ошибок не поддается счету и их очень сложно искать.
Про них я в основном и буду рассказывать.
Все проблемы в этой группе связанны с нарушением целостности логических связей между элементами базы:
- избыточная обработка невозможных состояний
- недостаточная обработка возможных состояний
- запутанные условия в выражениях
- зависящее от вендора поведение
- worst practices
Переходим к поиску проблем в схеме БД.
Первый документ, который стоит изучить: https://wiki.postgresql.org/wiki/Don't_Do_This
Там вы найдете небольшое количество правил, которые легко учесть при разработке, если вы не используете ORM.
Но на что еще нам стоит обратить внимание?
Покажу 10 из 1.5k правил.

Пара моментов, связанных с производительностью:
1) зеркальные индексы (a,b) и (b,a) или индексы с перестановкой
В общем случае невозможно определить оптимальный порядок колонок в индексе. При анализе стоит учесть все множество запросов в проекте, которые могли бы частично использовать многоколоночный индекс.
Но даже если таких запросов нет, они могут появиться.
В любом случае стоит избегать индексов одного типа, созданных по одним и тем же полям. Это замедлит обновление таблицы, увеличит нагрузку на диск, а значит заафектит производительность всей базы.

2) UNIQUE индекс, включающий поля неограниченного размера
Проверка уникальности сама по себе дорогая операция, которая будет тормозить изменение таблицы. Но кроме того, такой индекс таит в себе еще несколько опасных побочных эффектов.
Во-первых, полная уникальность длинной строки, скорее всего не имеет смысла с точки зрения архитектуры. Обычно нужны будут каки-то функции от текста - lowercase, tsvector, или что-то более сложное.
Во-вторых, в один прекрасный момент можно получить ошибку вот с таким хинтом:
Values larger than 1/3 of a buffer page cannot be indexed.

Поэтому рекомендуется строить такие индексы по какому-нибудь хешу

Несколько архитектурных issues:
3) заданное DEFAULT значение без ограничений NOT NULL
Самый распространенный кейс использования значений по умолчанию - отсутствие необходимости указывать значение при вставке данных.
Скорее всего это означает, что отсутствие значения нас не устроит.
Стоит обозначить это явно.

4) Константные DEFAULT значения для колонок, используемых в уникальных индексах
Может привести к возникновению ошибки при вставке строк без явного указания значений для таких колонок.
Скорее всего в тестах такой кейс не придет никому в голову, а значит найти причину будет сложно.

5) DEFAULT значение для колонок с внешним ключем
Кроме очевидного вопроса "зачем?" возникает 2 опасных кейса при вставке данных:
- значение DEFAULT есть во внешней таблице, но не соответствует бизнес логике. Это ломает связанность данных
- значения нет во внешней таблице, ошибка

6) Внешний ключ по SERIAL/IDENTITY колонкам
Мало того, что это не имеет физического смысла, это довольно вероятно со временем приведет к ошибкам при вставке, когда соответствующие значения во внешней таблице закончатся.

7) Уникальные TIMESTAMP колонки
Не имеет физического смысла в большинстве случаев, т.к. TIMESTAMP имеет точность в 1 микросекунду.
Кроме того, могут возникнуть проблемы при массовой вставке с использованием функций времени, фиксируемых на начало транзакции, например NOW()

8) Уникальный индекс включает другой уникальный индекс
Бессмысленно и имеет побочные эффекты, аналогичные (1)

9) Похожие имена
PostgreSQL приводит к нижнему регистру все имена, указанные без кавычек. Snowflake к верхнему регистру. Но имена в кавычках будут регистро-чувствительными.
"Field", "field" и field (превратится в FIELD в Snowflake) будут считаться разными идентификаторами, что может привести к ошибке

10) Неявная генерация имен
Для ограничений, индексов или других объектов, которые не требуют обязательного указания имени при создании, база данных подбирает имена самостоятельно.
В PostgreSQL для этого есть специальный алгоритм. Но если сгенерированные имена объектов похожи, к ним будут добавлены числовые индексы.
А это значит, что важен порядок создания этих объектов.
Поэтому на разных экземплярах базы имена одних и тех же объектов могут быть перепутаны.
Это может привести к очень печальным последствиям, например, при удалении объектов.
Но Snowflake ведет себя иначе. Он генерирует неуказанные явно имена случайным образом. При изменениях схемы такое поведение потребует большого количества внимательности и ручного труда.
Вчера я рассказал о 10 проблемах, которые можно встретить в схемах БД (DDL), а сегодня поговорим о том, что можно найти в самих запросах (DML).
Так же, как и вчера, я покажу 10 случайных правил из моей обширной полутора тысячной коллекции :)

Несколько заметок о производительности

1) count(DISTINCT unique)
Когда разработчик хочет посчитать количество уникальных значений, он скорее всего использует конструкцию count(DISTINCT).

Но если колонка, по которому требуется сделать выборку, является уникальным в силу уже имеющихся ограничений (уникальная колонка, результат работы функции в подзапросе и тд), то убрав DISTINCT в этом выражении, мы ускорим count в 100 раз.

2) Проверка на NULL значений после приведения типов
A IS NOT NULL
в 2 раза быстрее чем
A :: type IS NOT NULL

Причем тайпкаст может быть прийти из подзапроса и обнаружить это будет не так просто.

Если такая конструкция используется в массовых операциях при сканировании таблицы, то довольно просто можно ускорить запросы просто убрав приведение типов.
Как понимаете, такие запросы довольно просто получить при использовании различных ORM.

3) В условии JOIN ON не используется присоединяемая таблица
Довольно популярная ошибка, которую так же можно отнести и в разряд архитектурных. Встречается повсеместно при копипасте:
 ... t1 JOIN t2 ON t1.id = t.id 


Это приведет к лавинообразному росту: для каждой строки, где t1.id = t.id будут присоединяться ВСЕ данные из таблицы t2.
При аналитических запросах, где используются агрегационные функции довольно просто такое не заметить

4) JOIN таблиц без использования внешних ключей
 JOIN t2 ON t1.id = t2.id 

Если не существует внешнего ключа, описывающего связь t1.id и t2.id, то вполне возможно, что в запрос закралась ошибка, полученная при копипасте.

Эту проблему надо рассматривать вместе еще с одной DDL-проблемой: если есть внешний ключ, то исходящее поле стоит включить в индекс, т.к. по внешнему ключу скорее всего будет JOIN.
Целевая таблица внешнего ключа обязана иметь уникальный индекс, а в исходящей стоит создать обычный

JOIN по полям, не имеющих связи в виде внешнего ключа могут и не иметь подходящих индексов, что замедлит операцию.
Но эта ошибка выглядит более страшно с точки зрения архитектуры. Такой запрос вернет невалидный результат! Мы связали поля, которые не имеют логической связи.

5) порядок колонок в GROUP BY _МОЖЕТ_ иметь значение
Порядок колонок в группировке никак не влияет на содержимое результата. Но на производительность может влиять существенно. Результат _МОЖЕТ_ зависеть от кучи вещей:

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

Но в bigquery, говорят, все пошло еще дальше, и там рекомендуют самостоятельно заниматься расстановкой условий в WHERE:
2.3 WHERE clause: Expression order matters
https://wklytech.medium.com/bigquery-sql-optimization-c7a7db170c56
Я все еще очень надеюсь, что это fake news :)
Перейдем к архитектурным проблемам

6) LIMIT/OFFSET без ORDER BY
К сожалению не все знают, что в PostgreSQL нет сортировки по умолчанию. Это значит что в общем случае вы каждый раз можете получать случайный порядок строк. Не просто один и тот же случайный, а каждый раз новый

Если от порядка строк зависит бизнес логика внутри приложения, обязательно используйте ORDER BY

Ситуация ухудшается, если вы используете OFFSET/LIMIT. Пока оставим за скобками то, что не рекомендуется использовать эти конструкции.
Если порядок не определен, то используя OFFSET/LIMIT, вы можете получить случайный кусок данных, а совсем не тот, на который вы рассчитывали.

7) LIMIT применяется к запросу, возвращающему 1 строку
Это яркий пример наложения чрезмерных ограничений. Вроде ничего страшного нет. На данные LIMIT не повлияет. Да, но может повлиять на разработчика, который будет менять такой запрос.

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

8) OFFSET применяется к запросу, возвращающему 1 строку
А вот это уже действительно плохо. Запрос с OFFSET > 0 будет возвращать пустой результат, там где он на самом деле есть.
SELECT 1 OFFSET 1
А т.к. OFFSET, как правило, передается из приложения, получить пустоту особенно легко

9) псевдоним выражения совпадает с именем поля
 SELECT id + 1 AS id FROM t1 

Казалось бы, что тут такого? Но давайте снова подумаем о разработчике, который, быть может, и запроса самого не видит, а лишь использует результат в приложении.
 #define TRUE FALSE //Happy debugging suckers 


10) отсутствует ORDER BY для агрегационных функций
В PosgreSQL существует ряд функций, которые могут вернуть результат в виде массива или объекта. Например, array_agg. Мы помним, что порядок результатов у нас не гарантирован?
Ну так вот,
 array_agg(f) ... ORDER BY f 
не поможет...

Если мы хотим иметь гарантированно упорядоченный массив, нам нужно указать порядок внутри самой функции - array_agg(f ORDER BY f)
На всякий случай напомню, что это контент я готовлю для https://twitter.com/sqaunderhood, которым я рулю на этой неделе.
Богомерзкий твиттер приносит много боли, заставляя пилить все на 280 символов. Мне, привыкшему к длинным телегам, это больно :)
Поэтому вечером я скидываю все что понаписал сюда.

Но я дублирую (как могу) на английском в своем акке, поэтому, если захотите пошарить с англоговорящими коллегами, велкам https://twitter.com/antonrevyako