the presence or operation of another system • Development - change in one system require changes in another • Semantic - systems share identical concepts • Functional - overlapping responsibilities • Temporal - dependency based on time or sequence • Stamp - share more information than necessary • Latent or Indirect - shared knowledge of a third system
many others ◦ - blocking unaware gatekeeper risk ◦ ~ who is responsible ◦ - release coordination (more convoluted and risky) ◦ - does not scale well with larger number of teams and or services • + less complex with a minimum of teams and or services • + easier to enforce company wide rules
topic • + high degree of autonomy • + scales well with larger number of teams or services • - distributed code/scripts/pipeline configs • ~ less easy to enforce company wide rules
transactions • coordinator is determined by ◦ hash(transactional.id) / mod(len(__transaction_state partitions)) ◦ the resulting partition number, its leader will become the coordinator
starts using the same transactional ID as an active transaction. ERROR 4395 --- [producer-my-app-0] o.a.k.clients.producer.internals.Sender : [Producer clientId=producer-my-app-0, transactionalId=my-app-0] Aborting producer batches due to fatal error org.apache.kafka.common.errors.ProducerFencedException: Producer attempted an operation with an old epoch. Either there is a newer producer with the same transactionalId, or the producer's transaction has been expired by the broker.
Which results in the following transaction.ids my-app-4d567be0-3bb2-4b87-9b5d-a9440abd2dc9-0 my-app-4d567be0-3bb2-4b87-9b5d-a9440abd2dc9-1 my-app-4d567be0-3bb2-4b87-9b5d-a9440abd2dc9-2
@Transactional void orderProduct(OrderProductRequest request) { save(request) kafkaTemplate.send(toOrderCreatedEvent(request)) } } Annotations magically make things work .. or not? Request processing failed: org.springframework.beans.factory.NoUniqueBeanDefinitionExcepti on: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,kafkaTransactionManager
@Transactional(“transactionManager”) void orderProduct(OrderProductRequest request) { save(request) kafkaTemplate.send(toOrderCreatedEvent(request)) } } And the kafka transaction will magically also work ? Yes, because • KafkaTransactionManager synchronizes itself with the JpaTransactionManager • The JpaTransactionManager commits first • Upon success the KafkaTransactionManager commits
@Transactional("kafkaTransactionManager") publish(OrderProductRequest request) { this.kafkaTemplate.send(toOrderShipped(request)); } } With Spring Boot, it is only necessary to set the spring.kafka.producer.transaction-id-prefix property - Spring Boot will automatically configure a KafkaTransactionManager bean and wire it into the listener container. Unnecessary
new ExponentialBackOffWithMaxRetries(6); bo.setInitialInterval(1_000L); bo.setMultiplier(2.0); bo.setMaxInterval(10_000L); return new DefaultErrorHandler(myRecoverer, bo); } This will retry after 1, 2, 4, 8, 10, 10 seconds, before calling the recoverer.
number of topics and increase the number of partitions • + easier to manager when things go wrong DLT per service • - uses less topics and partitions • + harder to manage when things go wrong ◦ due to mixed formats ◦ due to mixed sources