В последнюю неделю апреля и в первых числах мая мы с семьёй (жена, я и двое детей - 6 и 3.5 лет) отправились в отпуск на Селигер.
Наверное самым запоминающимся моментом была ночь с 30 апреля на 1 мая. Мы как раз съехали с турбазы и отправились пожить с палатками в лесу на берегу озера. Фотка сделана около 1:00, когда я вел маящегося животом младшего погреться у печки во второй палатке. На следующее утро мы поняли, что с двумя детьми жить в лесу в снегу пока рановато и через новогодний заснеженный лес (на видео) эвакуировались на турбазу.
#природа
#селигер
Наверное самым запоминающимся моментом была ночь с 30 апреля на 1 мая. Мы как раз съехали с турбазы и отправились пожить с палатками в лесу на берегу озера. Фотка сделана около 1:00, когда я вел маящегося животом младшего погреться у печки во второй палатке. На следующее утро мы поняли, что с двумя детьми жить в лесу в снегу пока рановато и через новогодний заснеженный лес (на видео) эвакуировались на турбазу.
#природа
#селигер
❤9
This media is not supported in your browser
VIEW IN TELEGRAM
Ещё один занятный момент произошел когда мы остановились показать детям бобриную плотину на одном из ручьёв, который пересекала дорога, ведущая с турбазы. Из леса на водопой вышел кабанчик, спокойно попил и потрусил по своим делам дальше.
#природа
#селигер
#природа
#селигер
🔥4
❤7🔥1
На выходных был на рыбалке на Рыбинском водохранилище, открывал летний водомоторный сезон.
В сумме я провел на воде около 16 часов, кидая спиннинг и исследуя место впадения речки Рени в Рыбинское водохранилище. Ехал я за щукой, но она не клевала, совсем: ни единой отметины зубов на приманках я не увидел. Клевал окунь, два (грамм по 300) оказались достаточно голодными, чтобы проглотить щучью приманку.
Несмотря на малое количество рыбы, поездка удалась: сильно прокачался в управлении лодкой, обкидал новый спиннинг, попал в ливень посреди залива, покупался.
#природа
#рыбинка
В сумме я провел на воде около 16 часов, кидая спиннинг и исследуя место впадения речки Рени в Рыбинское водохранилище. Ехал я за щукой, но она не клевала, совсем: ни единой отметины зубов на приманках я не увидел. Клевал окунь, два (грамм по 300) оказались достаточно голодными, чтобы проглотить щучью приманку.
Несмотря на малое количество рыбы, поездка удалась: сильно прокачался в управлении лодкой, обкидал новый спиннинг, попал в ливень посреди залива, покупался.
#природа
#рыбинка
🔥8👍2❤1
Последние полтора года я работал в банке ПСБ, но пришло время расставаться. Был получен крайне интересный опыт, правда больше организационный: я увидел как может быть организована разработка в крупной организации с серьезным контролем качества и сохранности данных.
За полтора года пользования внутренними платформенными решениями и следования корпоративным стандартам кода я неслабо забыл базовые вещи, потому далее будет серия постов-шпаргалок по разным вопросам, которые могут встретиться на собеседованиях на сеньора-шарписта.
Основная цель - осознать и запомнить некоторые моменты, которые не держатся в голове, заодно получив материалы для освежения в памяти базы.
Далее подобные посты будут публиковаться под тегом #собес
За полтора года пользования внутренними платформенными решениями и следования корпоративным стандартам кода я неслабо забыл базовые вещи, потому далее будет серия постов-шпаргалок по разным вопросам, которые могут встретиться на собеседованиях на сеньора-шарписта.
Основная цель - осознать и запомнить некоторые моменты, которые не держатся в голове, заодно получив материалы для освежения в памяти базы.
Далее подобные посты будут публиковаться под тегом #собес
👍7🔥2
И начну я серию постов к собеседованиям #собес с базовой базы: первых четырёх нормальных форм (НФ) реляционки. Вообще, я как-то больше по денормализации, потому попробую снабдить каждую из НФ короткими ремарками, как я её попирал.
Первая нормальная форма: в таблицах есть первичные ключи, отсутствуют дубли, к тому же нет составных данных, то есть не допускается хранение данных в одной записи в виде массива или просто утрамбованных в текстовый тип данных с разделителем. Про то, как я мучался с дублями в таблице в ~500Гб можно почитать тут. А еще у меня на одном из рабочих проектов был опыт, когда одно из хранимых мной полей могло быть long, long[], guid или objectId (монговский формат "уникального" id). Сохранение в виде строки не проходило по требованиям к объему базы, раскидывание по разным строкам - по требованиям к производительности. В итоге я изобразил свой бинарный формат и хранил тупо байтики.
Поясню: long - id сущности из другой системы, например - "инфаркт" (система была для медтеха). long[] - жесткая сцепка двух понятий, например - "инфаркт в анамнезе", что несет несколько другой смысл для принятия врачебных решений (делалась СППВР).
Вторая нормальная форма. База пребывает в первой форме, а в дополнение - данные во всех столбцах зависят от первичного ключа целиком. Нарушал, сознательно, получилось удачно. Таблица, в которой хранятся логи того, что произошло с заказом. Ключ у таблицы составной, пусть будет id заказа + id изменения, уникальный в рамках данного заказа. Мне понадобилось иметь быстрый доступ к статусу заказа, ну я и стал его проставлять во всех записях, относящихся к заказу. В результате можно получить некоторый выигрыш в получении статуса: указываем id заказа, после чего первая встреченная нами запись гарантировано содержит нужную нам информацию. Вообще, это дикость и варварство, не надо так делать, но в том конкретном случае получилось отлично за счёт некоторых нюансов, про которые я когда-нибудь напишу отдельно.
Третья нормальная форма + нормальная форма Бойса-Кодда - база пребывает в первой и второй НФ, к тому же отсутствует один из любимых (в т.ч. мной) способов посрезать углы: вместо внешнего ключа, указывающего на таблицу-справочник, писать значение из несостоявшегося справочника прямо в основную таблицу. Встречал подобное десятки раз, обычно так сохраняют статусы, меняющиеся со временем или обвешивают записи системой тегов для группировки или фильтрации. И сам так делал и буду делать, если дозволяют принятые стандарты.
Для себя не вижу смысла разделять их между собой. С т.з. формального определения разница существенная, а вот стандартное нарушение едино.
Четвертая нормальная форма - база пребывает во всех предшествующих нормальных формах, а в дополнение в таблицах отсутствуют данные, зависящие только от первичного ключа, но при этом не имеющие между собой логических связей. Этот принцип я нарушал множество раз и с особым цинизмом. Один из примеров - был у меня маленький проект, связанный с системой построения отчётов FastReports (я писал о ней). Мне нужно было решить проблему хранения файлов отчётов и информации о них: название, тег (идентификатор отчёта, по которому его запрашивает фронт), группа, сам файл макета отчёта, сериализованный в base64 (да, хранить такое в базе - дикость, но иногда приходится).
По хорошему, у меня должно было быть три или четыре таблицы, связанные внешними ключами, что-то вроде: reports_info, reports_tags, reports_groups, reports_base64_files. Но я все утрамбовал в одну таблицу, вынимая по тегу отчёт, имеющий самую свежую дату. Примитивно и сердито.
Из кучи просмотренного в интернете по нормальным формам, самой толковой оказалась информация на сайте ИТМО: первая и вторая, третья, четвертая.
Первая нормальная форма: в таблицах есть первичные ключи, отсутствуют дубли, к тому же нет составных данных, то есть не допускается хранение данных в одной записи в виде массива или просто утрамбованных в текстовый тип данных с разделителем. Про то, как я мучался с дублями в таблице в ~500Гб можно почитать тут. А еще у меня на одном из рабочих проектов был опыт, когда одно из хранимых мной полей могло быть long, long[], guid или objectId (монговский формат "уникального" id). Сохранение в виде строки не проходило по требованиям к объему базы, раскидывание по разным строкам - по требованиям к производительности. В итоге я изобразил свой бинарный формат и хранил тупо байтики.
Поясню: long - id сущности из другой системы, например - "инфаркт" (система была для медтеха). long[] - жесткая сцепка двух понятий, например - "инфаркт в анамнезе", что несет несколько другой смысл для принятия врачебных решений (делалась СППВР).
Вторая нормальная форма. База пребывает в первой форме, а в дополнение - данные во всех столбцах зависят от первичного ключа целиком. Нарушал, сознательно, получилось удачно. Таблица, в которой хранятся логи того, что произошло с заказом. Ключ у таблицы составной, пусть будет id заказа + id изменения, уникальный в рамках данного заказа. Мне понадобилось иметь быстрый доступ к статусу заказа, ну я и стал его проставлять во всех записях, относящихся к заказу. В результате можно получить некоторый выигрыш в получении статуса: указываем id заказа, после чего первая встреченная нами запись гарантировано содержит нужную нам информацию. Вообще, это дикость и варварство, не надо так делать, но в том конкретном случае получилось отлично за счёт некоторых нюансов, про которые я когда-нибудь напишу отдельно.
Третья нормальная форма + нормальная форма Бойса-Кодда - база пребывает в первой и второй НФ, к тому же отсутствует один из любимых (в т.ч. мной) способов посрезать углы: вместо внешнего ключа, указывающего на таблицу-справочник, писать значение из несостоявшегося справочника прямо в основную таблицу. Встречал подобное десятки раз, обычно так сохраняют статусы, меняющиеся со временем или обвешивают записи системой тегов для группировки или фильтрации. И сам так делал и буду делать, если дозволяют принятые стандарты.
Для себя не вижу смысла разделять их между собой. С т.з. формального определения разница существенная, а вот стандартное нарушение едино.
Четвертая нормальная форма - база пребывает во всех предшествующих нормальных формах, а в дополнение в таблицах отсутствуют данные, зависящие только от первичного ключа, но при этом не имеющие между собой логических связей. Этот принцип я нарушал множество раз и с особым цинизмом. Один из примеров - был у меня маленький проект, связанный с системой построения отчётов FastReports (я писал о ней). Мне нужно было решить проблему хранения файлов отчётов и информации о них: название, тег (идентификатор отчёта, по которому его запрашивает фронт), группа, сам файл макета отчёта, сериализованный в base64 (да, хранить такое в базе - дикость, но иногда приходится).
По хорошему, у меня должно было быть три или четыре таблицы, связанные внешними ключами, что-то вроде: reports_info, reports_tags, reports_groups, reports_base64_files. Но я все утрамбовал в одну таблицу, вынимая по тегу отчёт, имеющий самую свежую дату. Примитивно и сердито.
Из кучи просмотренного в интернете по нормальным формам, самой толковой оказалась информация на сайте ИТМО: первая и вторая, третья, четвертая.
Telegram
Эшу быдлокодит
Палантир. Часть 14. Дубли в базе. Боль и страдания.
#палантир@eshu_coding
Одной из первых проблем были дубли в данных: одно и то же сообщение засасывалось более одного раза.
В какой-то момент я принял решение просто наплевать на них: ну есть у меня 15%…
#палантир@eshu_coding
Одной из первых проблем были дубли в данных: одно и то же сообщение засасывалось более одного раза.
В какой-то момент я принял решение просто наплевать на них: ну есть у меня 15%…
🔥11
Небольшая рефлексия полученного ранее опыта.
У меня был в работе нагруженный метод, собирающий и обогащающий информацию с 50+ http вызовов к другим микросервисам, при том часть вызовов шло с использованием данных с предыдущих ответов.
Изначально все вызовы я пустил строго последовательно, рассудив, что отладка параллельных вызовов превратится в ад (так в последствии и оказалось).
Когда большая часть багов была выловлена, я занялся распараллеливанием и оптимизацией. В итоге число вызовов сократилось до ~30 и получился занятный граф вызовов, напоминающий протекание реки через сеть островов. Моей ошибкой было ограничиться кодом, забив на визуализацию. В итоге часть информации запрашивается два раза, к счастью это по факту не на что не повлияло. Вывод на будущее: сначала рисуем схему, потом пишем код, или хотя бы параллельно.
Ещё нерешенным для меня остаётся вопрос, как архитектурно правильно организовать кеширование в сервисе с множеством источников данных. Жёсткого стандарта на эту тему не было, кешировалось все каждым разработчиком на том слое, где это казалось рациональным. В итоге получилось не очень красиво: при перемещении между реализациями методов, инфраструктурный код взаимодействия с кешем оказался замешан с бизнесовым. Я бы попробовал ограничиться жёстким стандартом: кешируются, только финальные аггрегации данных, подготовленные к отдаче запросившему и только данные, получаемые из внешних источников. Кеширование промежуточных агрегаций данных в общем случае я бы запретил.
#архитектура
У меня был в работе нагруженный метод, собирающий и обогащающий информацию с 50+ http вызовов к другим микросервисам, при том часть вызовов шло с использованием данных с предыдущих ответов.
Изначально все вызовы я пустил строго последовательно, рассудив, что отладка параллельных вызовов превратится в ад (так в последствии и оказалось).
Когда большая часть багов была выловлена, я занялся распараллеливанием и оптимизацией. В итоге число вызовов сократилось до ~30 и получился занятный граф вызовов, напоминающий протекание реки через сеть островов. Моей ошибкой было ограничиться кодом, забив на визуализацию. В итоге часть информации запрашивается два раза, к счастью это по факту не на что не повлияло. Вывод на будущее: сначала рисуем схему, потом пишем код, или хотя бы параллельно.
Ещё нерешенным для меня остаётся вопрос, как архитектурно правильно организовать кеширование в сервисе с множеством источников данных. Жёсткого стандарта на эту тему не было, кешировалось все каждым разработчиком на том слое, где это казалось рациональным. В итоге получилось не очень красиво: при перемещении между реализациями методов, инфраструктурный код взаимодействия с кешем оказался замешан с бизнесовым. Я бы попробовал ограничиться жёстким стандартом: кешируются, только финальные аггрегации данных, подготовленные к отдаче запросившему и только данные, получаемые из внешних источников. Кеширование промежуточных агрегаций данных в общем случае я бы запретил.
#архитектура
👍6🔥2
Распишу один из самых часто задаваемых вопросов на собеседованиях - принципы SOLID.
S: Single Responsibility Principle (Принцип единственной ответственности). Класс должен решать только какую-то одну задачу. Видел в интернетах много высосанных из пальца примеров, попробую дать примеры из жизни. К основному нарушению этого принципа я бы отнес замешивание в одном месте бизнесового и инфраструктурного кода. Имеем класс с методом с какой-то ядреной бизнес логикой, и тут, ВНЕЗАПНО нам нужно получить дополнительные данные. Не выходя из метода открываем подключение к БД, формируем запрос, читаем ответ, продолжаем бизнес логику. Чувствуете запах?:) Или другой вариант - замешиваем в одном классе получение данных из брокера и их бизнесовую обработку.
Ну и классический пример - антипаттерн singleton. Плоха не единственность экземпляра класса, а самоплальная логика обеспечения единственности.
O: Open-Closed Principle (Принцип открытости-закрытости). Классы должны быть открыты для расширения и закрыты для модификации. Самый простой пример, попирающий этот принцип - использование else if бесконечных размеров, куда по мере развития добавляются новые вариации ветвлений со своей логикой. Самое интересное начинается спустя несколько лет, когда приходится спиливать лишние загибы ветвлений или что-то изменять, оставляя часть функционала неизменной. Альтернатива - больше использовать следующий принцип.
L: Liskov Substitution Principle (Принцип подстановки Барбары Лисков). Объекты в программе должны быть заменяемы их наследниками без изменения корректности программы. В целом - самый очевидный принцип, который осознанно нарушают достаточно редко.
I: Interface Segregation Principle (Принцип разделения интерфейса). Классы не должны реализовывать методы только для совместимости с абстракцией предка. Самый простой путь нарушить предыдущий принцип - реализовывать часть методов только для сохранения совместимости. Как правило, это делается спустя рукава или без полного погружения в контекст.
D: Dependency Inversion Principle (Принцип инверсии зависимостей). Классы должны зависеть не от других классов, а от интерфейсов, чтобы в любой момент можно было подменить реализацию. Очевидная на бумаге вещь, но до чего же лениво ей следовать!
#собес
#solid
S: Single Responsibility Principle (Принцип единственной ответственности). Класс должен решать только какую-то одну задачу. Видел в интернетах много высосанных из пальца примеров, попробую дать примеры из жизни. К основному нарушению этого принципа я бы отнес замешивание в одном месте бизнесового и инфраструктурного кода. Имеем класс с методом с какой-то ядреной бизнес логикой, и тут, ВНЕЗАПНО нам нужно получить дополнительные данные. Не выходя из метода открываем подключение к БД, формируем запрос, читаем ответ, продолжаем бизнес логику. Чувствуете запах?:) Или другой вариант - замешиваем в одном классе получение данных из брокера и их бизнесовую обработку.
Ну и классический пример - антипаттерн singleton. Плоха не единственность экземпляра класса, а самоплальная логика обеспечения единственности.
O: Open-Closed Principle (Принцип открытости-закрытости). Классы должны быть открыты для расширения и закрыты для модификации. Самый простой пример, попирающий этот принцип - использование else if бесконечных размеров, куда по мере развития добавляются новые вариации ветвлений со своей логикой. Самое интересное начинается спустя несколько лет, когда приходится спиливать лишние загибы ветвлений или что-то изменять, оставляя часть функционала неизменной. Альтернатива - больше использовать следующий принцип.
L: Liskov Substitution Principle (Принцип подстановки Барбары Лисков). Объекты в программе должны быть заменяемы их наследниками без изменения корректности программы. В целом - самый очевидный принцип, который осознанно нарушают достаточно редко.
I: Interface Segregation Principle (Принцип разделения интерфейса). Классы не должны реализовывать методы только для совместимости с абстракцией предка. Самый простой путь нарушить предыдущий принцип - реализовывать часть методов только для сохранения совместимости. Как правило, это делается спустя рукава или без полного погружения в контекст.
D: Dependency Inversion Principle (Принцип инверсии зависимостей). Классы должны зависеть не от других классов, а от интерфейсов, чтобы в любой момент можно было подменить реализацию. Очевидная на бумаге вещь, но до чего же лениво ей следовать!
#собес
#solid
👍7❤1
Эшу быдлокодит
Распишу один из самых часто задаваемых вопросов на собеседованиях - принципы SOLID. S: Single Responsibility Principle (Принцип единственной ответственности). Класс должен решать только какую-то одну задачу. Видел в интернетах много высосанных из пальца…
По горячим следам пример к букве I - разделению интерфейсов.
Например: пишем бота в телеграмме, у нас есть сущность - сообщение. Хочется(если честно - не очень) сделать какой-то интерфейс для типичных методов для работы с ним IMessage с методами GetText, GetAttachedMedia, LogToDB и т.д.
Но у нас есть нюанс: сообщения бывают входящие и исходящие. Исходящие после формирования надо отправлять в телегу, то есть нужен метод Send.
Но добавление этого метода в общий интерфейс заставит нас делать какую-то нерабочую фигню во входящем сообщении, потому интерфейсы надо разделить: ICommonMessage, а от него наследуются два интерфейса: IIncomeMessage и IOutcomeMessage, объявляющие свои специфические методы.
#собес
#solid
Например: пишем бота в телеграмме, у нас есть сущность - сообщение. Хочется
Но у нас есть нюанс: сообщения бывают входящие и исходящие. Исходящие после формирования надо отправлять в телегу, то есть нужен метод Send.
Но добавление этого метода в общий интерфейс заставит нас делать какую-то нерабочую фигню во входящем сообщении, потому интерфейсы надо разделить: ICommonMessage, а от него наследуются два интерфейса: IIncomeMessage и IOutcomeMessage, объявляющие свои специфические методы.
#собес
#solid
🔥5