Pull to refresh
35
0.2
Валерий Вырва @valery1707

Java backend

Send message

А не рассматривали вынести логику из консюмера и продюсера в сервисы не завязанные на Kafka в принципе?
Чтобы тестировать логику в отрыве от транспорта:

  • Метод в консюмере просто транслирует полученный объект в сервис - однострочник консюмера можно не тестировать вовсе, а тестируемый сервис не зависит от кафки и на вход просто принимает объекты

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

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

Пример кода

То есть вместо ```java @Slf4j @Service @RequiredArgsConstructor public class UserConsumer {

private final UserService userService;
private final NotUserService notUserService;
private final UserHandler userHandler;
private final UserMapper userMapper;
private final List<String> userCodes;

@KafkaListener(groupId = "userConsumerGroupId",
        clientIdPrefix = "UserConsumer",
        topics = {"user-topic"},
        containerFactory = "kafkaListenerContainerFactory")
public void consume(UserInfoRecord userInfoRecord) {
    log.info("Тут разнообразная логика обработки");
    log.info("Cохранение в бд?");
    log.info("Работа с внешними сервисами?");
    log.info("Вызов мапперов?");
    log.info("Отправка информации в Kafka?");
}

}

@Slf4j
@Service
@RequiredArgsConstructor
public class UserProducer {

private final UserService userService;
/** Если у нас какой-то персистинг сообщений перед отправкой или обработка ошибок отправки */
private final KafkaMessageService kafkaMessageService;
/** Если мы просто отправляем - используем KafkaProducer, например, у нас по бОльшей части используетс AVRO */
private final KafkaProducer<String, SpecificRecordBase> kafkaProducer;
/** Но есть места, где используется JSON вместо AVRO */
private final KafkaProducer<String, String> kafkaJsonProducer;
private final ApplicationProperties applicationProperties;

public void sendUserInfoMessage(User user, Set<Integer> managerIds) {
  log.info("Тут логика отправки");
  log.info("Обычно сначала идет сборка модели для отправки.")
  log.info("Но может быть и какая-то более сложная логика.");
  var record = UserInfoRecord.newBuilder().build();
  log.info("Тут может быть как непосредственно отправка в Kafka");
  log.info("Так и реализация outbox-паттерна.");
  kafkaMessageService.persistMessageToSend(applicationProperties.userTopic(), record);
}

}

сделать так:
```java
@Slf4j
@Service
@RequiredArgsConstructor
public class UserConsumer {

    private final UserConsumerService service;

    @KafkaListener(groupId = "userConsumerGroupId",
            clientIdPrefix = "UserConsumer",
            topics = {"user-topic"},
            containerFactory = "kafkaListenerContainerFactory")
    public void consume(UserInfoRecord userInfoRecord) {
	    service.consume(userInfoRecord);
    }
}

@Slf4j
@Service
@RequiredArgsConstructor
public class UserConsumerService {
    private final UserService userService;
    private final NotUserService notUserService;
    private final UserHandler userHandler;
    private final UserMapper userMapper;
    private final List<String> userCodes;

    public void consume(UserInfoRecord userInfoRecord) {
        log.info("Тут разнообразная логика обработки");
        log.info("Cохранение в бд?");
        log.info("Работа с внешними сервисами?");
        log.info("Вызов мапперов?");
        log.info("Отправка информации в Kafka?");
    }
}

@Slf4j
@Service
@RequiredArgsConstructor
public class UserProducerService {

    private final KafkaSender sender;

    public void sendUserInfoMessage(User user, Set<Integer> managerIds) {
      log.info("Обычно сначала идет сборка модели для отправки.")
      log.info("Но может быть и какая-то более сложная логика.");
      var record = UserInfoRecord.newBuilder().build();
      sender.sendUserInfoMessage(record);
    }
}

@Slf4j
@Service
@RequiredArgsConstructor
public class KafkaSender {

    private final UserService userService;
    /** Если у нас какой-то персистинг сообщений перед отправкой или обработка ошибок отправки */
    private final KafkaMessageService kafkaMessageService;
    /** Если мы просто отправляем - используем KafkaProducer, например, у нас по бОльшей части используетс AVRO */
    private final KafkaProducer<String, SpecificRecordBase> kafkaProducer;
    /** Но есть места, где используется JSON вместо AVRO */
    private final KafkaProducer<String, String> kafkaJsonProducer;
    private final ApplicationProperties applicationProperties;

    public void sendUserInfoMessage(UserInfoRecord record) {
      log.info("Тут логика отправки");
      log.info("Тут может быть как непосредственно отправка в Kafka");
      log.info("Так и реализация outbox-паттерна.");
      kafkaMessageService.persistMessageToSend(applicationProperties.userTopic(), record);
    }
}

Странно что не упомянуто основное ограничение хеш-индексов: они могут помогать только в случае простых проверках на равенство.
То есть при поиске x = 500 он поможет, а при поиске x >= 500 уже нет.

и у них не было стандартов и документации?

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

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

Я начал погружаться в компьютерные RPG с Might and Magic VI: The Mandate of Heaven (1998) и там был открытый мир - персонаж мог идти куда угодно и его ничего не останавливало.
Даже море было препятствием только первое время и только игро-механически - персонаж банально тонул из-за чего терял HP и мог умереть, но потом получал "хождение по воде" или "полёт" и море уже не было проблемой.
В разных областях монстры были разной сложности - не справляешься в прямом столкновении - можно пробовать стелсом, можно набегами, можно отвлекать на себя по несколько противников, а если противник один, но сильный, то переход в пошаговый режим позволял выполнить больше шагов чем противник и этим нанести больше урона.
Да, можно было свернуть в сторону и раскачаться так что вернувшись оказаться "перекачанным", но это был мой выбор как игрока: я мог идти равномерно и получить равномерный рост сложности, и я же мог прыгнуть в область "не по рангу", пробежать там зайчиком получить кучку квестов и прочих зацепок на будущее, и вернуться в "плановое" место с лучшим шмотом или заклинаниями и банально уровнем, за счёт чего упростить собственную жизнь.
И это, на мой взгляд, нормально - да, где-то сложнее, а где-то проще.

Я из Казахстана (был, пока не переехал в РФ) и там вполне в ходу выражение "в СНГ"

ТС это "топик стартер" = то есть тот с кого началось обсуждение в ветке.
И от него была постановка что ошибка и на уровне валидатора и на уровне БД должны для клиента выглядить одинаково:

Ошибка конечно одна и та же.

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

Ну если обсуждаеть совершенно не то что изначально постулировалось ТС, то конечно можно получить другой вывод :)

Тогда всё становится просто: валидатор - валидирует, а БД бьёт по рукам, если программист ошибся.

Можно и так, то вот по рукам будут бить клиентов - пришёл создать пользователя, а получил невнятную ошибку от БД.

Только они на эти саммиты специально собираются, а не просто приходят всей толпой в одно место чтобы заниматься чем-то отличным от "саммитов и переговоров", чтобы эти самые "саммиты и переговоры" выполнялись просто из-за того что они находятся в одном месте?

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

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

Валидатор на уникальность - должен проверять только на уникальность.

Валидация формата - задача отдельного валидатора, который никак не связан с уникальностью.

Всё описанное будет в любом случае так как ошибка уникальности на стороне БД всё равно вероятна и её всё равно нужно конвертировать в сообщение идентичное сообщению от валидатора (это не моё желание - так описал задачу ТС).

А вот кода валидатора на уникальность - не будет.
Как и тестов к нему.

И в итоге если убрать валидатор, то ничего не измениться с точки зрения клиента, только будет на один запрос к БД меньше и в принципе кода меньше.
А в чём профит от того что валидатор на уникальный email есть?

Поддерживаю @breninsul - без проверки уникальности на стороне БД будет шляпа так как параллельные запросы на создание могут оба увидеть что такого значения ещё нет в БД и оба перейти к этапу записи - уникальность на уровне БД обеспечивается средствами БД.

Я вот переезжал недавно с Xiaomi Mi6 на OnePlus 9R 5G и процесс вышел совсем не таким уж и удобным как мне бы хотелось:

  • СМС: я смог забэкапить сторонней софтиной, но не смотря на её заверения об успешном их восстановлении, их не видно в стандартном приложении

  • Историю звонков перенес сторонней софтиной

  • Часть старых приложений просто отсутствует

  • Часть старых приложений сменили имя и владельца

  • Часть старых приложений есть только на альтернативных сторах

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

  • Многие приложения просто не имеют своего облачного хранилища для настроек и не поддерживают Гугловое

  • Даже там где есть возможность "просто залогиниться" всё равно нужно доводить до привычных разные локальные настройки

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

Для меня переезд на новый телефон - это стресс. Сравнимый со сменой места проживания в пределах города - вроде всё знакомое, но всё равно другое и кучу всего нужно с собой везти, а что-то потеряется.

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

Триггеров не видно, хотя хранимки есть.
Про планы на JSON уже ответили, но судя по этому ответу XML даже не в планах.
Про регулярки я в доке ничего не встрелил, как просто поиском, так и анализом доступных операторов сравнения.

Про CTE в документации есть параграф:

Common Table Expressions (CTE) — это именованный временный результирующий набор, который существует в рамках одного оператора и на который можно ссылаться в последующих запросах в рамках этого оператора, возможно, несколько раз.
Более подробное описание будет при ближайшем обновлении документа.

Рекурсивность упоминается рядом с CTE:

RECURSIVE WITH PUMP
Получение очередной записи из результатов WITH в рамках исполнения WITH с рекурсией.

Про оконные функции там ничего нет - перечислены только скалярные и агрегатные.
Термины XML и JSON так же не встречаются.

1
23 ...

Information

Rating
2,249-th
Location
Воронеж, Воронежская обл., Россия
Date of birth
Registered
Activity