#Retryable with Spring #Transactional - spring

I have a project whrere I need to use retriable on a transactional method. Let's say I want to update some client informations and I need to retry updating 2 times untill .
I put in my pom dependecy for
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
In my #Configuration class I have #EnableRetry.
In my #Service class I defined a method:
> #Retryable(value = CannotAcquireLockException.class, maxAttempts = 3,
> backoff = #Backoff(delay = 1000,multiplier = 2,maxDelay = 5000))
> #Transactional
> public void updateClient (String info) throws Exception {
> updateClientFromDB(info);
> }
I test my method from Junit5 and seems that #Retryable doesn't work . I lock a row in db and when I run the test it seems does not retry, it loop endless.
> #ExtendWith(SpringExtension.class)
> #ContextConfiguration(classes = {DSConfig.class })
> #ActiveProfiles({ "test" })
> #Slf4j class Test {
>
> #Test
> void retryTest() {
> String info = "Test";
> updateClient(info); } }

does updateClientFromDB throw CannotAcquireLockException?

Related

Failed to create or validate data directory for EmbeddedKafkaBroker

I am trying to run a simple unit test using #EmbeddedKafka Annotation.
As a reference, I am following the below spring documentation
https://docs.spring.io/spring-kafka/reference/html/#embedded-kafka-annotation
#RunWith(SpringRunner.class)
#DirtiesContext
#EmbeddedKafka(brokerProperties = "log.dir=/kafka-logs", partitions = 1,
topics = {
"dare_policy_created"})
#Slf4j
public class ConsumerTest {
#Autowired
private EmbeddedKafkaBroker embeddedKafka;
#Test
public void someTest() {
Map<String, Object> consumerProps = KafkaTestUtils.consumerProps("testGroup", "true", this.embeddedKafka);
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
ConsumerFactory<Integer, String> cf = new DefaultKafkaConsumerFactory<>(consumerProps);
Consumer<Integer, String> consumer = cf.createConsumer();
this.embeddedKafka.consumeFromAnEmbeddedTopic(consumer, "dare_policy_created");
ConsumerRecords<Integer, String> replies = KafkaTestUtils.getRecords(consumer);
//assertThat(replies.count()).isGreaterThanOrEqualTo(1);
}
}
I was trying to define the log.dir #EmbeddedKafka(brokerProperties = "log.dir= ") because I was getting an error when running the Test.
I tried :
log.dir=/kafka-logs
log.dir=real_path_to_my_project/kafka-logs
...
But every time I run the test I get this error :
kafka.server.LogDirFailureChannel.error - Failed to create or validate data directory /kafka-logs java.io.IOException: Failed to load /kafka-logs during broker startup
kafka.log.LogManager.fatal - Shutdown broker because none of the specified log dirs from /kafka-logs can be created or validated
I was able to resolve the issue by removing an explicit dependency on kafka-client.
I had below dependency in my pom
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.3.0</version>
</dependency>

How to use resilience4j on calling method?

I tried to use spring retry for Circuit breaking and retry as below and it is working as expected but issue is unable to configure "maxAttempts/openTimeout/resetTimeout" as env variables (error is should be constants). My question is how use resilience4j to achieve the below requirement?
also please suggest there is a way to pass env variables to "maxAttempts/openTimeout/resetTimeout".
#CircuitBreaker(value = {
MongoServerException.class,
MongoSocketException.class,
MongoTimeoutException.class
MongoSocketOpenException.class},
maxAttempts = 2,
openTimeout = 20000L ,
resetTimeout = 30000L)
public void insertDocument(ConsumerRecord<Long, GenericRecord> consumerRecord){
retryTemplate.execute(args0 -> {
LOGGER.info(String.format("Inserting record with key -----> %s", consumerRecord.key().toString()));
BasicDBObject dbObject = BasicDBObject.parse(consumerRecord.value().toString());
dbObject.put("_id", consumerRecord.key());
mongoCollection.replaceOne(<<BasicDBObject with id>>, getReplaceOptions());
return null;
});
}
#Recover
public void recover(RuntimeException t) {
LOGGER.info(" Recovering from Circuit Breaker ");
}
dependencies used are
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
You are not using resilience4j, but spring-retry.
You should adapt the title of your question.
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.waitDurationInOpenState(Duration.ofMillis(20000))
.build();
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("mongoDB");
RetryConfig retryConfig = RetryConfig.custom().maxAttempts(3)
.retryExceptions(MongoServerException.class,
MongoSocketException.class,
MongoTimeoutException.class
MongoSocketOpenException.class)
.ignoreExceptions(CircuitBreakerOpenException.class).build();
Retry retry = Retry.of("helloBackend", retryConfig);
Runnable decoratedRunnable = Decorators.ofRunnable(() -> insertDocument(ConsumerRecord<Long, GenericRecord> consumerRecord))
.withCircuitBreaker(circuitBreaker)
.withRetry(retry)
.decorate();
String result = Try.runRunnable(decoratedRunnable )
.recover(exception -> ...).get();

How to remove the entire cache, and then pre-populate the cache?

Can someone tell me what is the problem with below implementation. I'm trying to delete the entire cache, secondly, I then want to pre-populate/prime the cache. However, what I've below is only deleting both caches, but not pre-populating/priming the cache, when the two methods are executed. Any idea?
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
#Cacheable(cacheNames = "cacheOne")
List<User> cacheOne() throws Exception {...}
#Cacheable(cacheNames = "cacheOne")
List<Book> cacheTwo() throws Exception {...}
#Caching (
evict = {
#CacheEvict(cacheNames = "cacheOne", allEntries = true),
#CacheEvict(cacheNames = "CacheTwo", allEntries = true)
}
)
void clearAndReloadEntireCache() throws Exception
{
// Trying to reload cacheOne and cacheTwo inside this method
// Is this even possible? if not what is the correct approach?
cacheOne();
cacheTwo();
}
I've spring boot application (v1.4.0), more importantly, utilizing the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.0.0</version>
</dependency>
If you call the clearAndReloadEntireCache() method, only this method will be processed by the caching interceptor. Calling other methods of the same object: cacheOne() and cacheTwo() will not cause cache interception at runtime, although both of them are annotated with #Cacheable.
You could achieve desired functionality by reloading cacheOne and cacheTwo with two method calls shown below:
#Caching(evict = {#CacheEvict(cacheNames = "cacheOne", allEntries = true, beforeInvocation = true)},
cacheable = {#Cacheable(cacheNames = "cacheOne")})
public List<User> cleanAndReloadCacheOne() {
return cacheOne();
}
#Caching(evict = {#CacheEvict(cacheNames = "cacheTwo", allEntries = true, beforeInvocation = true)},
cacheable = {#Cacheable(cacheNames = "cacheTwo")})
public List<Book> cleanAndReloadCacheTwo() {
return cacheTwo();
}

Mock only selected properties in Spring Environment

I want to be able to use a test properties files and only override a few properties. Having to override every single property will get ugly fast.
This is the code I am using to test my ability to mock properties and use existing properties in a test case
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
#TestPropertySource(
locations = { "classpath:myapp-test.properties" },
properties = { "test.key = testValue" })
public class EnvironmentMockedPropertiesTest {
#Autowired private Environment env;
// #MockBean private Environment env;
#Test public void testExistingProperty() {
// some.property=someValue
final String keyActual = "some.property";
final String expected = "someValue";
final String actual = env.getProperty(keyActual);
assertEquals(expected, actual);
}
#Test public void testMockedProperty() {
final String keyMocked = "mocked.test.key";
final String expected = "mockedTestValue";
when(env.getProperty(keyMocked)).thenReturn(expected);
final String actual = env.getProperty(keyMocked);
assertEquals(expected, actual);
}
#Test public void testOverriddenProperty() {
final String expected = "testValue";
final String actual = env.getProperty("test.key");
assertEquals(expected, actual);
}
}
What I find is:
#Autowired private Environment env;
testExistingProperty() and testOverriddenProperty() pass
testMockedProperty() fails
#MockBean private Environment env;
testMockedProperty() passes
testExistingProperty() and testOverriddenProperty() fail
Is there a way to achieve what I am aiming for?
Dependencies:
<spring.boot.version>1.4.3.RELEASE</spring.boot.version>
...
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!-- Starter for testing Spring Boot applications with libraries including JUnit,
Hamcrest and Mockito -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
</dependency>
Ok i have made this work, you need to use Mockito to accompish what you are looking for:
Maven Dependency
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.6.4</version>
</dependency>
Test Class Set up
import static org.mockito.Mockito.*;
import static org.springframework.test.util.AopTestUtils.getTargetObject;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
#TestPropertySource(
locations = { "classpath:myapp-test.properties" },
properties = { "test.key = testValue" })
public class AnswerTest {
// This will be only for injecting, we will not be using this object in tests.
#Autowired
private Environment env;
// This is the reference that will be used in tests.
private Environment envSpied;
// Map of properties that you intend to mock
private Map<String, String> mockedProperties;
#PostConstruct
public void postConstruct(){
mockedProperties = new HashMap<String, String>();
mockedProperties.put("mocked.test.key_1", "mocked.test.value_1");
mockedProperties.put("mocked.test.key_2", "mocked.test.value_2");
mockedProperties.put("mocked.test.key_3", "mocked.test.value_3");
// We use the Spy feature of mockito which enabled partial mocking
envSpied = Mockito.spy((Environment) getTargetObject(env));
// We mock certain retrieval of certain properties
// based on the logic contained in the implementation of Answer class
doAnswer(new CustomAnswer()).when(envSpied).getProperty(Mockito.anyString());
}
Test case
// Testing for both mocked and real properties in same test method
#Test public void shouldReturnAdequateProperty() {
String mockedValue = envSpied.getProperty("mocked.test.key_3");
String realValue = envSpied.getProperty("test.key");
assertEquals(mockedValue, "mocked.test.value_3");
assertEquals(realValue, "testValue");
}
Implementation of Mockito's Answer interface
// Here we define what should mockito do:
// a) return mocked property if the key is a mock
// b) invoke real method on Environment otherwise
private class CustomAnswer implements Answer<String>{
#Override
public String answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] arguments = invocationOnMock.getArguments();
String parameterKey = (String) arguments[0];
String mockedValue = mockedProperties.get(parameterKey);
if(mockedValue != null){
return mockedValue;
}
return (String) invocationOnMock.callRealMethod();
}
}
}
Try it out, and let me know if all is clear here.

How to use DeleteByQuery plugin with embedded ES 2.3.3

I have run ES 2.3.3 in an embedded fashion but I'm unable to invoke the DeleteByQuery action due to the described exception. I added the DeleteByQuery plugin to my classpath and also set the plugin.types settings for my not but it is still not working.
My Maven dependencies:
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>delete-by-query</artifactId>
<version>2.3.3</version>
</dependency>
My ES Setup:
Settings elasticsearchSettings = Settings.settingsBuilder()
.put("threadpool.index.queue_size", -1)
.put("path.home", options.getDirectory())
.put("plugin.types", DeleteByQueryPlugin.class.getName())
.build();
NodeBuilder builder = NodeBuilder.nodeBuilder();
node = builder.local(true).settings(elasticsearchSettings).node();
Invocation of the action which is used to truncate the index.
DeleteByQueryRequestBuilder builder = new DeleteByQueryRequestBuilder(node.client(), DeleteByQueryAction.INSTANCE);
builder.setIndices(indexName).setQuery(QueryBuilders.matchAllQuery()).execute().addListener(new ActionListener<DeleteByQueryResponse>() {
public void onResponse(DeleteByQueryResponse response) {
if (log.isDebugEnabled()) {
log.debug("Deleted index {" + indexName + "}. Duration " + (System.currentTimeMillis() - start) + "[ms]");
}
sub.onCompleted();
};
#Override
public void onFailure(Throwable e) {
log.error("Deleting index {" + indexName + "} failed. Duration " + (System.currentTimeMillis() - start) + "[ms]", e);
sub.onError(e);
}
});
Exception that I'm seeing:
Caused by: java.lang.IllegalStateException: failed to find action [org.elasticsearch.action.deletebyquery.DeleteByQueryAction#7c1ed3a2] to execute
at org.elasticsearch.client.node.NodeClient.doExecute(NodeClient.java:56) ~[elasticsearch-2.3.3.jar:2.3.3]
at org.elasticsearch.client.support.AbstractClient.execute(AbstractClient.java:359) ~[elasticsearch-2.3.3.jar:2.3.3]
I noticed that the Node builder invokes the node constructor with an empty plugin list. I extended the Node class in order to invoke this (protected) constructor.
public class ESNode extends Node {
protected ESNode(Settings settings, Collection<Class<? extends Plugin>> plugins) {
super(InternalSettingsPreparer.prepareEnvironment(settings, null), Version.CURRENT, plugins);
}
}
Using the ESNode all the needed plugin was loaded.
Set<Class<? extends Plugin>> classpathPlugins = new HashSet<>();
classpathPlugins.add(DeleteByQueryPlugin.class);
node = new ESNode(settings, classpathPlugins).start();
This may not be ideal but so far it is working just fine.

Resources