Skip to content

Commit 475432c

Browse files
refactor: optimize hashAttributes method using MurmurHash3 and improve null handling
1 parent 59c871d commit 475432c

File tree

1 file changed

+34
-42
lines changed

1 file changed

+34
-42
lines changed

core-api/src/main/java/com/optimizely/ab/cmab/service/DefaultCmabService.java

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.optimizely.ab.cmab.service;
22

3-
import java.security.MessageDigest;
4-
import java.security.NoSuchAlgorithmException;
53
import java.util.Collections;
64
import java.util.HashMap;
75
import java.util.List;
@@ -11,6 +9,7 @@
119
import org.slf4j.Logger;
1210

1311
import com.optimizely.ab.OptimizelyUserContext;
12+
import com.optimizely.ab.bucketing.internal.MurmurHash3;
1413
import com.optimizely.ab.cmab.client.CmabClient;
1514
import com.optimizely.ab.config.Attribute;
1615
import com.optimizely.ab.config.Experiment;
@@ -131,48 +130,41 @@ private String getCacheKey(String userId, String ruleId) {
131130
}
132131

133132
private String hashAttributes(Map<String, Object> attributes) {
134-
try {
135-
// Sort attributes to ensure consistent hashing
136-
TreeMap<String, Object> sortedAttributes = new TreeMap<>(attributes);
137-
138-
// Create a simple string representation
139-
StringBuilder sb = new StringBuilder();
140-
sb.append("{");
141-
boolean first = true;
142-
for (Map.Entry<String, Object> entry : sortedAttributes.entrySet()) {
143-
if (!first) {
144-
sb.append(",");
145-
}
146-
sb.append("\"").append(entry.getKey()).append("\":");
147-
if (entry.getValue() instanceof String) {
148-
sb.append("\"").append(entry.getValue()).append("\"");
149-
} else {
150-
sb.append(entry.getValue());
151-
}
152-
first = false;
153-
}
154-
sb.append("}");
155-
156-
// Generate MD5 hash
157-
MessageDigest md = MessageDigest.getInstance("MD5");
158-
byte[] hash = md.digest(sb.toString().getBytes());
159-
160-
// Convert to hex string
161-
StringBuilder hexString = new StringBuilder();
162-
for (byte b : hash) {
163-
String hex = Integer.toHexString(0xff & b);
164-
if (hex.length() == 1) {
165-
hexString.append('0');
166-
}
167-
hexString.append(hex);
133+
if (attributes == null || attributes.isEmpty()) {
134+
return "empty";
135+
}
136+
137+
// Sort attributes to ensure consistent hashing
138+
TreeMap<String, Object> sortedAttributes = new TreeMap<>(attributes);
139+
140+
// Create a simple string representation
141+
StringBuilder sb = new StringBuilder();
142+
sb.append("{");
143+
boolean first = true;
144+
for (Map.Entry<String, Object> entry : sortedAttributes.entrySet()) {
145+
if (entry.getKey() == null) continue; // Skip null keys
146+
147+
if (!first) {
148+
sb.append(",");
168149
}
169-
return hexString.toString();
170-
171-
} catch (NoSuchAlgorithmException e) {
172-
if (logger != null) {
173-
logger.warn("Failed to hash attributes", e);
150+
sb.append("\"").append(entry.getKey()).append("\":");
151+
152+
Object value = entry.getValue();
153+
if (value == null) {
154+
sb.append("null");
155+
} else if (value instanceof String) {
156+
sb.append("\"").append(value).append("\"");
157+
} else {
158+
sb.append(value.toString());
174159
}
175-
throw new RuntimeException("MD5 algorithm not available", e);
160+
first = false;
176161
}
162+
sb.append("}");
163+
164+
String attributesString = sb.toString();
165+
int hash = MurmurHash3.murmurhash3_x86_32(attributesString, 0, attributesString.length(), 0);
166+
167+
// Convert to hex string to match your existing pattern
168+
return Integer.toHexString(hash);
177169
}
178170
}

0 commit comments

Comments
 (0)