This is an automated email from the ASF dual-hosted git repository.
adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 91e0187fbac HDDS-10782. Replace GSON with Jackson in EventQueue (#8949)
91e0187fbac is described below
commit 91e0187fbac0ecde2262c4e351669bb855d4eed4
Author: Anastasia Kostryukova <[email protected]>
AuthorDate: Thu Oct 9 13:20:43 2025 +0300
HDDS-10782. Replace GSON with Jackson in EventQueue (#8949)
---
hadoop-hdds/framework/pom.xml | 8 +-
.../hadoop/hdds/server/events/EventQueue.java | 122 +++++++++++++++++----
2 files changed, 102 insertions(+), 28 deletions(-)
diff --git a/hadoop-hdds/framework/pom.xml b/hadoop-hdds/framework/pom.xml
index fed0f112553..5c368525010 100644
--- a/hadoop-hdds/framework/pom.xml
+++ b/hadoop-hdds/framework/pom.xml
@@ -34,6 +34,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
@@ -50,10 +54,6 @@
<groupId>com.github.jnr</groupId>
<artifactId>jnr-posix</artifactId>
</dependency>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
index 02778d5e89f..8ea8f6136c3 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/events/EventQueue.java
@@ -17,20 +17,32 @@
package org.apache.hadoop.hdds.server.events;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
-import com.google.gson.ExclusionStrategy;
-import com.google.gson.FieldAttributes;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.Message;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.hadoop.hdds.scm.net.InnerNode;
import org.apache.hadoop.hdds.scm.net.NodeImpl;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.Time;
@@ -59,9 +71,7 @@ public class EventQueue implements EventPublisher,
AutoCloseable {
private boolean isRunning = true;
- private static final Gson TRACING_SERIALIZER = new GsonBuilder()
- .setExclusionStrategies(new DatanodeDetailsGsonExclusionStrategy())
- .create();
+ private static final ObjectWriter TRACING_SERIALIZER = buildSerializer();
private boolean isSilent = false;
private final String threadNamePrefix;
@@ -74,21 +84,88 @@ public EventQueue(String threadNamePrefix) {
this.threadNamePrefix = threadNamePrefix;
}
+ private static String serializeObject(Object payload) {
+ try {
+ return TRACING_SERIALIZER.writeValueAsString(payload);
+ } catch (JsonProcessingException e) {
+ return String.valueOf(payload);
+ }
+ }
+
+ private static ObjectWriter buildSerializer() {
+ ObjectMapper mapper = new ObjectMapper()
+ .enable(SerializationFeature.INDENT_OUTPUT)
+ .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
+ .addMixIn(NodeImpl.class, DatanodeDetailsJacksonMixIn.class);
+
+ SimpleModule module = new SimpleModule();
+
+ module.addSerializer(Message.class, new JsonSerializer<Message>() {
+ @Override
+ public void serialize(Message msg, JsonGenerator gen, SerializerProvider
sp) throws IOException {
+ gen.writeObject(convertMessageToMap(msg));
+ }
+
+ private Object convertMessageToMap(Message msg) {
+ Map<String, Object> fieldMap = new LinkedHashMap<>();
+ for (Map.Entry<Descriptors.FieldDescriptor, Object> e :
msg.getAllFields().entrySet()) {
+ String fieldName = e.getKey().getName();
+ Object value = convertField(e.getKey(), e.getValue());
+ fieldMap.put(fieldName, value);
+ }
+ return fieldMap;
+ }
+
+ /**
+ * Handles protobuf message fields.
+ */
+ private Object convertField(Descriptors.FieldDescriptor fd, Object
value) {
+ if (fd.isRepeated()) {
+ List<?> fields = (List<?>) value;
+ List<Object> result = new ArrayList<>();
+ for (Object field : fields) {
+ result.add(convertSingleValue(fd, field));
+ }
+ return result;
+ }
+ return convertSingleValue(fd, value);
+ }
+
+ /**
+ * Converts a single field value to a JSON representation.
+ */
+ private Object convertSingleValue(Descriptors.FieldDescriptor fd, Object
field) {
+ switch (fd.getJavaType()) {
+ case STRING:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ case BOOLEAN:
+ return field;
+ case ENUM:
+ return ((Descriptors.EnumValueDescriptor) field).getName();
+ case BYTE_STRING:
+ ByteString bs = (ByteString) field;
+ return Base64.getEncoder().encodeToString(bs.toByteArray());
+ case MESSAGE:
+ return convertMessageToMap((Message) field);
+ default:
+ return String.valueOf(field);
+ }
+ }
+ });
+
+ mapper.registerModule(module);
+ return mapper.writerWithDefaultPrettyPrinter();
+ }
+
// The field parent in DatanodeDetails class has the circular reference
// which will result in Gson infinite recursive parsing. We need to exclude
// this field when generating json string for DatanodeDetails object
- static class DatanodeDetailsGsonExclusionStrategy
- implements ExclusionStrategy {
- @Override
- public boolean shouldSkipField(FieldAttributes f) {
- return f.getDeclaringClass() == NodeImpl.class
- && f.getName().equals("parent");
- }
-
- @Override
- public boolean shouldSkipClass(Class<?> aClass) {
- return false;
- }
+ abstract static class DatanodeDetailsJacksonMixIn {
+ @JsonIgnore
+ abstract InnerNode getParent();
}
/**
@@ -196,18 +273,17 @@ public <PAYLOAD, EVENT_TYPE extends Event<PAYLOAD>> void
fireEvent(
eventCount.incrementAndGet();
if (eventExecutorListMap != null) {
-
for (Map.Entry<EventExecutor, List<EventHandler>> executorAndHandlers :
eventExecutorListMap.entrySet()) {
-
for (EventHandler handler : executorAndHandlers.getValue()) {
queuedCount.incrementAndGet();
if (LOG.isTraceEnabled()) {
+ String jsonPayload = serializeObject(payload);
LOG.trace(
"Delivering [event={}] to executor/handler {}:
<json>{}</json>",
event.getName(),
executorAndHandlers.getKey().getName(),
- TRACING_SERIALIZER.toJson(payload).replaceAll("\n", "\\\\n"));
+ jsonPayload.replaceAll("\n", "\\\\n"));
} else if (LOG.isDebugEnabled()) {
LOG.debug("Delivering [event={}] to executor/handler {}: {}",
event.getName(),
@@ -216,10 +292,8 @@ public <PAYLOAD, EVENT_TYPE extends Event<PAYLOAD>> void
fireEvent(
}
executorAndHandlers.getKey()
.onMessage(handler, payload, this);
-
}
}
-
} else {
if (!isSilent) {
LOG.warn("No event handler registered for event {}", event);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]