Skip to content

Commit 4dc4b87

Browse files
authored
Merge pull request #9 from pagopa/NOD-706-error-tracing
[NOD-706] fix: set retry mechanism for event storage resilience
2 parents b658b24 + 007a847 commit 4dc4b87

File tree

6 files changed

+108
-16
lines changed

6 files changed

+108
-16
lines changed

host.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"logging": {
2929
"logLevel": {
3030
"default": "Error",
31-
"Function.EventHubNodoVerifyKOEventToDSProcessor": "Warning"
31+
"Function.EventHubNodoVerifyKOEventToDSProcessor": "Information"
3232
},
3333
"applicationInsights": {
3434
"samplingSettings": {

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>it.gov.pagopa</groupId>
77
<artifactId>nodoverifykotodatastore</artifactId>
8-
<version>0.1.4</version>
8+
<version>0.1.4-7-NOD-706-error-tracing</version>
99
<packaging>jar</packaging>
1010

1111
<name>Nodo Verify KO to datastore Fn</name>

src/main/java/it/gov/pagopa/nodoverifykotodatastore/NodoVerifyKOEventToDataStore.java

+45-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.microsoft.azure.functions.ExecutionContext;
44
import com.microsoft.azure.functions.OutputBinding;
55
import com.microsoft.azure.functions.annotation.*;
6+
import it.gov.pagopa.nodoverifykotodatastore.exception.AppException;
67
import it.gov.pagopa.nodoverifykotodatastore.util.Constants;
78
import it.gov.pagopa.nodoverifykotodatastore.util.ObjectMapperUtils;
89
import lombok.NonNull;
@@ -21,7 +22,10 @@
2122
*/
2223
public class NodoVerifyKOEventToDataStore {
2324

25+
private static final Integer MAX_RETRY_COUNT = 5;
26+
2427
@FunctionName("EventHubNodoVerifyKOEventToDSProcessor")
28+
@ExponentialBackoffRetry(maxRetryCount = 5, maximumInterval = "00:15:00", minimumInterval = "00:00:10")
2529
public void processNodoVerifyKOEvent (
2630
@EventHubTrigger(
2731
name = "NodoVerifyKOEvent",
@@ -39,8 +43,16 @@ public void processNodoVerifyKOEvent (
3943
@NonNull OutputBinding<List<Object>> documentdb,
4044
final ExecutionContext context) {
4145

46+
String errorCause = null;
47+
boolean isPersistenceOk = true;
48+
int retryIndex = context.getRetryContext() == null ? -1 : context.getRetryContext().getRetrycount();
49+
4250
Logger logger = context.getLogger();
43-
logger.log(Level.INFO, () -> String.format("Persisting [%d] events...", events.size()));
51+
logger.log(Level.FINE, () -> String.format("Persisting [%d] events...", events.size()));
52+
53+
if (retryIndex == MAX_RETRY_COUNT) {
54+
logger.log(Level.WARNING, () -> String.format("[ALERT][LAST RETRY][VerifyKOToDS] Performing last retry for event ingestion: InvocationId [%s], Events: %s", context.getInvocationId(), events));
55+
}
4456

4557
try {
4658
if (events.size() == properties.length) {
@@ -75,20 +87,45 @@ public void processNodoVerifyKOEvent (
7587
eventsToPersist.add(event);
7688
}
7789

90+
logger.log(Level.INFO, () -> String.format("Performing event ingestion: InvocationId [%s], Retry Attempt [%d], Events: %s", context.getInvocationId(), retryIndex, extractTraceForEventsToPersist(eventsToPersist)));
91+
7892
// save all events in the retrieved batch in the storage
7993
persistEventBatch(logger, documentdb, eventsToPersist);
8094
} else {
81-
logger.log(Level.SEVERE, () -> String.format("[ALERT][VerifyKOToDS] AppException - Error processing events, lengths do not match: [events: %d - properties: %d]", events.size(), properties.length));
95+
isPersistenceOk = false;
96+
errorCause = String.format("[ALERT][VerifyKOToDS] AppException - Error processing events, lengths do not match: [events: %d - properties: %d]", events.size(), properties.length);
8297
}
8398
} catch (IllegalArgumentException e) {
84-
logger.log(Level.SEVERE, () -> "[ALERT][VerifyKOToDS] AppException - Illegal argument exception on cosmos nodo-verify-ko-events msg ingestion at " + LocalDateTime.now() + " : " + e);
99+
isPersistenceOk = false;
100+
errorCause = "[ALERT][VerifyKOToDS] AppException - Illegal argument exception on cosmos nodo-verify-ko-events msg ingestion at " + LocalDateTime.now() + " : " + e;
85101
} catch (IllegalStateException e) {
86-
logger.log(Level.SEVERE, () -> "[ALERT][VerifyKOToDS] AppException - Missing argument exception on nodo-verify-ko-events msg ingestion at " + LocalDateTime.now() + " : " + e);
102+
isPersistenceOk = false;
103+
errorCause = "[ALERT][VerifyKOToDS] AppException - Missing argument exception on nodo-verify-ko-events msg ingestion at " + LocalDateTime.now() + " : " + e;
87104
} catch (Exception e) {
88-
logger.log(Level.SEVERE, () -> "[ALERT][VerifyKOToDS] AppException - Generic exception on cosmos nodo-verify-ko-events msg ingestion at " + LocalDateTime.now() + " : " + e.getMessage());
105+
isPersistenceOk = false;
106+
errorCause = "[ALERT][VerifyKOToDS] AppException - Generic exception on cosmos nodo-verify-ko-events msg ingestion at " + LocalDateTime.now() + " : " + e.getMessage();
89107
}
108+
109+
if (!isPersistenceOk) {
110+
String finalErrorCause = errorCause;
111+
logger.log(Level.SEVERE, () -> finalErrorCause);
112+
throw new AppException(errorCause);
113+
}
90114
}
91115

116+
@SuppressWarnings({"unchecked"})
117+
private static String extractTraceForEventsToPersist(List<Object> eventsToPersist) {
118+
return Arrays.toString(eventsToPersist.stream()
119+
.map(event -> {
120+
Map<String, Object> eventMap = (Map<String, Object>) event;
121+
String rowKey = getEventField(eventMap, "id", String.class, "null");
122+
String partitionKey = getEventField(eventMap, Constants.PARTITION_KEY_EVENT_FIELD, String.class, "null");
123+
Long eventTimestamp = getEventField(eventMap, "faultBean.timestamp", Long.class, -1L);
124+
return String.format("{PartitionKey: %s, RowKey: %s, EventTimestamp: %d}", partitionKey, rowKey, eventTimestamp);
125+
})
126+
.toArray());
127+
}
128+
92129
private String fixDateTime(String faultBeanTimestamp) {
93130
int dotIndex = faultBeanTimestamp.indexOf('.');
94131
if (dotIndex != -1) {
@@ -116,7 +153,7 @@ private String replaceDashWithUppercase(String input) {
116153

117154
private void persistEventBatch(Logger logger, OutputBinding<List<Object>> documentdb, List<Object> eventsToPersistCosmos) {
118155
documentdb.setValue(eventsToPersistCosmos);
119-
logger.info("Done processing events");
156+
logger.log(Level.FINE, () -> "Done processing events");
120157
}
121158

122159
private String generatePartitionKey(Map<String, Object> event, String insertedDateValue) {
@@ -127,7 +164,8 @@ private String generatePartitionKey(Map<String, Object> event, String insertedDa
127164
getEventField(event, Constants.PSP_ID_EVENT_FIELD, String.class, Constants.NA);
128165
}
129166

130-
private <T> T getEventField(Map<String, Object> event, String name, Class<T> clazz, T defaultValue) {
167+
@SuppressWarnings({"rawtypes"})
168+
private static <T> T getEventField(Map<String, Object> event, String name, Class<T> clazz, T defaultValue) {
131169
T field = null;
132170
List<String> splitPath = List.of(name.split("\\."));
133171
Map eventSubset = event;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package it.gov.pagopa.nodoverifykotodatastore.exception;
2+
3+
public class AppException extends RuntimeException {
4+
5+
public AppException(String message) {
6+
super(message);
7+
}
8+
}

src/test/java/it/gov/pagopa/nodoverifykotodatastore/NodoVerifyKOEventToDataStoreTest.java

+33-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package it.gov.pagopa.nodoverifykotodatastore;
22

3-
import static org.junit.jupiter.api.Assertions.assertEquals;
4-
import static org.junit.jupiter.api.Assertions.assertTrue;
3+
import static org.junit.jupiter.api.Assertions.*;
54
import static org.mockito.Mockito.*;
65

7-
import java.sql.Array;
86
import java.util.ArrayList;
97
import java.util.HashMap;
108
import java.util.List;
@@ -16,6 +14,7 @@
1614
import com.fasterxml.jackson.databind.ObjectMapper;
1715
import com.microsoft.azure.functions.ExecutionContext;
1816
import com.microsoft.azure.functions.OutputBinding;
17+
import it.gov.pagopa.nodoverifykotodatastore.exception.AppException;
1918
import it.gov.pagopa.nodoverifykotodatastore.util.Constants;
2019
import it.gov.pagopa.nodoverifykotodatastore.util.LogHandler;
2120
import it.gov.pagopa.nodoverifykotodatastore.util.TestUtil;
@@ -143,7 +142,7 @@ void runKo_invalidNumberOfProperties() {
143142
properties[1].put("prop1-with-dash", "2");
144143

145144
// execute logic
146-
function.processNodoVerifyKOEvent(events, properties, document, context);
145+
assertThrows(AppException.class, () -> function.processNodoVerifyKOEvent(events, properties, document, context));
147146

148147
// test assertion
149148
assertTrue(logHandler.getLogs().contains("Error processing events, lengths do not match: [events: 1 - properties: 2]"));
@@ -170,7 +169,7 @@ void runKo_missingFaultBeanTimestamp() {
170169
properties[0].put("prop1-with-dash", "1");
171170

172171
// execute logic
173-
function.processNodoVerifyKOEvent(events, properties, document, context);
172+
assertThrows(AppException.class, () -> function.processNodoVerifyKOEvent(events, properties, document, context));
174173

175174
// test assertion
176175
assertTrue(logHandler.getLogs().contains("java.lang.IllegalStateException"));
@@ -179,7 +178,7 @@ void runKo_missingFaultBeanTimestamp() {
179178
@SuppressWarnings("unchecked")
180179
@Test
181180
@SneakyThrows
182-
void runKo_() {
181+
void runKo_genericError() {
183182
// mocking objects
184183
Logger logger = Logger.getLogger("NodoVerifyKOEventToDataStore-test-logger");
185184
LogHandler logHandler = new LogHandler();
@@ -198,12 +197,39 @@ void runKo_() {
198197
properties[0].put("prop1-with-dash", "1");
199198

200199
// execute logic
201-
function.processNodoVerifyKOEvent(events, properties, document, context);
200+
assertThrows(AppException.class, () -> function.processNodoVerifyKOEvent(events, properties, document, context));
202201

203202
// test assertion
204203
assertTrue(logHandler.getLogs().contains("[ALERT][VerifyKOToDS] AppException - Generic exception on cosmos nodo-verify-ko-events msg ingestion"));
205204
}
206205

206+
@SuppressWarnings("unchecked")
207+
@Test
208+
@SneakyThrows
209+
void runKo_notGenerablePartitionKey() {
210+
// mocking objects
211+
Logger logger = Logger.getLogger("NodoVerifyKOEventToDataStore-test-logger");
212+
LogHandler logHandler = new LogHandler();
213+
logger.addHandler(logHandler);
214+
when(context.getLogger()).thenReturn(logger);
215+
OutputBinding<List<Object>> document = (OutputBinding<List<Object>>) mock(OutputBinding.class);
216+
217+
// generating input
218+
String eventInStringForm = TestUtil.readStringFromFile("events/event_ko_2.json");
219+
List<String> events = new ArrayList<>();
220+
events.add(eventInStringForm);
221+
Map<String, Object>[] properties = new HashMap[1];
222+
properties[0] = new HashMap<>();
223+
properties[0].put("prop1_without_dash", true);
224+
properties[0].put("prop1-with-dash", "1");
225+
226+
// execute logic
227+
assertThrows(AppException.class, () -> function.processNodoVerifyKOEvent(events, properties, document, context));
228+
229+
// test assertion
230+
assertTrue(logHandler.getLogs().contains("[ALERT][VerifyKOToDS] AppException - Illegal argument exception on cosmos nodo-verify-ko-events msg ingestion"));
231+
}
232+
207233
public String convertWithStream(List<Object> listOfMaps) {
208234
return listOfMaps.stream()
209235
.map(obj -> new TreeMap<>((Map<String, Object>) obj))
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"id": "uuid-001",
3+
"version": "2",
4+
"debtorPosition": {
5+
"modelType": "2",
6+
"noticeNumber": "302040000090000000",
7+
"amount": "50.00"
8+
},
9+
"creditor": {
10+
"idPA": "77777777777",
11+
"ccPost": "777777777777",
12+
"idBrokerPA": "77777777777",
13+
"idStation": "77777777777_01"
14+
},
15+
"faultBean": {
16+
"faultCode": "PPT_STAZIONE_INT_PA_ERRORE_RESPONSE",
17+
"description": "EC service error at 2023-01-01T12:00:00",
18+
"timestamp": "2023-12-12T18:34:39.860654"
19+
}
20+
}

0 commit comments

Comments
 (0)