All streams
Search
Write a publication
Pull to refresh
22
0
Алексей @pankraty

Разработчик

Send message

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

Не освещен вопрос возможности / удобства использования в системах контроля версий — тут SQLite и прочие бинарные форматы жестко проигрывают человекочитаемым.

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

Эх, в кинотеатрах бы такую практику ввести. Платишь деньги, а все равно будь добр откушать рекламы минут на 15.

Напомнило о баге, который я репортил в Гитлаб, когда на коммит, сделанный 14 декабря, 15 марта пишется "2 months ago". Тоже небось со StackOverflow взяли пример, где автор не учел несколько пограничных случаев…

Не имеет значения, в какой СУБД хранятся данные, если она поддерживает транзакции. Вот тут вариант реализации расписан более детально (http://www.kamilgrzybek.com/design/the-outbox-pattern/). Но мы все дальше уходим в оффтопик.

Так в этом и суть транзакции, чтобы откатить все сразу, если что-то отказало. Если это не требуется, то не надо исполнять логику в виде транзакции — и тогда какая разница, это один батч, некоторые части которого отказали, или несколько REST-запросов, некоторые из которых отказали?
При этом зеркальный вариант — когда транзакция все-таки необходима — несколькими REST-запросами реализовать затруднительно. И это тоже надо учитывать, выбирая между REST и RPC.

Чтобы продолжать функционировать в случае неработающей очереди, при этом сохранив гарантию отправки сообщения, существует паттерн guaranteed delivery (https://www.enterpriseintegrationpatterns.com/patterns/messaging/GuaranteedMessaging.html).


Отправляющая сторона хранит у себя сообщения до тех пор, пока очередь не примет его. Сохранение сообщения происходит в той же транзакции, что и сохранение данных, поэтому если сохранение произошло, то сообщение уже не потеряется (разве что "протухнет"). Отметка о прочтении проставится, когда очередь заработает, и теоретически может получиться, что очередь сообщение приняла и отправила сообщение, но не смогла закоммитить транзакцию с отметкой о прочтении — так что сообщение отправится более одного раза.


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


мы вновь попытаемся выполнить через какой-то интервал времени (Какой? Решает фронт?)

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

Т.е. условный Твитбук будет диктовать властям страны, кого угнетать разрешено, а кого нет, на основании собственных представлений, а не на основании законов этой страны? Например, власти отсталой Центрально-Океанской Республики недемократично сажают педофилов в тюрьму, а прогрессивный Твитбук называет это нарушением прав людей педосексуальной ориентации и на этом основании запрещает пользоваться собственными продуктами.
Впрочем, это мы и так уже наблюдаем в полный рост.

Или, скажем, судебный процесс. Ваше ПО нарушает права человека, поэтому… Тадам! Вы не имеете права использовать библиотеку YYY, и обязаны удалить все ссылки на нее из источников.
А не "прекратить нарушать права человека, определенные в Конвенции… ", как некоторые могли по наивности подумать.

Я вот так и представляю диалог:


  • Шеф, есть библиотека ХХХ, которая как раз решает нашу проблему YYY. Может, заюзаем ее?
  • Нет, она выпущена под лицензией Гиппократа, а мы тут права человека собираемся нарушать, так что нет, сорян, нельзя.

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


Во внешней библиотеки определены 2 класса, не имеющие общего предка, но имеющие несколько идентичных полей, с которыми должен работать наш метод.
Будь это наши классы, это можно было бы решить добавлением интерфейса с необходимыми полями, но т.к. это внешняя библиотека — не вариант.


Решение:


public class ExternalClassA
{
  // lots of properties
  public string PropertyA {get; }
  public int PropertyB { get; }
  // lots of other properties
}

public class ExternalClassB
{
  // lots of properties
  public string PropertyA {get; }
  public int PropertyB { get; }
  // lots of other properties
}

public class OurClass
{
  public void DoSomeWork(ExternalClassA subject)
  {
     DoSomeWorkInternal((dynamic) subject);
  }

  public void DoSomeWork(ExternalClassB subject)
  {
     DoSomeWorkInternal((dynamic) subject);
  }

  private void DoSomeWorkInternal(dynamic subject)
  {
     // here we can safely operate with subject.PropertyA, subject.PropertyB
  }
}

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

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

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

Раньше, когда я регулярно работал в CAD, я всех агитировал за темную тему по той причине, что на темном фоне глаз легко различает гораздо больше цветов, а разными цветами удобно кодировать различные смысловые слои. На белом фоне тонкие линии коричневого, темно-фиолетового, темно-синего цвета выглядят практически неотличимыми от черного, а многие светлые (желтый, розовый) еле видны. А на черном фоне и темные, и светлые линии хорошо различимы, гораздо проще работать.


Что интересно, программировал я в то время на светлом фоне и не парится совершенно. Только пару лет назад решил попробовать поработать с темной темой дольше пяти минут и постепенно перестроился, хотя и фанатом не стал. Глаза вроде бы устают меньше.

Да нет, я прекрасно понимаю, почему их нет. Просто неоднократно сталкивался с тем, что их наличие теоретически могло бы сделать запрос намного проще/нагляднее/понятнее.
Про отсутствие STRING_AGG до версии 2017 и связанные с этим костыли даже упоминать не хочется...

Это оконные функции, а речь про агрегатные. Они немного по-разному работают и не всегда взаимозамеяемы.

VIEW простая, в ней "всего лишь" фильтрация по результату оконной функции.


Что-то вроде


select * from (
  select id, status, date, rank() over (partition by id order by date desc) r
  from raw_statuses
) t
where r = 1

Как же не хватает агрегатных функций типа FIRST и LAST. Все способы обойти это выглядят костылями в той или иной степени...

О да, с "проталкиванием" параметров во VIEW буквально на днях столкнулся в полный рост.


Есть, скажем, таблица с историей статусов, и на ее основе создана VIEW, показывающая последний статус для каждой сущности. И если запрашивать данные по ID, план получается хороший, с index scan, запрос быстрый:


select v.last_status from status_view v where v.id = 123

Но если это же условие применяется в join-е, и даже если запись, к которой происходит join, будет строго одна в силу других условий, планировщик не парится, и делает full table scan, по всем миллионам записей:


select v.status 
from entity e
inner join status_view v on v.id = e.id
where e.number = 'xxx'

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

Information

Rating
Does not participate
Location
Саратов, Саратовская обл., Россия
Date of birth
Registered
Activity