Skip to content

Commit 1ec49ff

Browse files
APPENG-656: change twContext.put() to always update values (#44)
* APPENG-656: change twContext.put() to always update values regardless if the old and new values are equal. Change TwContextAttributeChangeListener to TwContextAttributePutListener as a result. * APPENG-656: update bean name in the auto configuration.
1 parent 8936ba3 commit 1ec49ff

File tree

10 files changed

+59
-49
lines changed

10 files changed

+59
-49
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
66
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.0.0] - 2023-09-22
9+
10+
### Changed
11+
* When using `twContext.put()` it now always stores the new value to attributes. It no longer checks if old value is not equal to the new one.
12+
This is needed to support use cases where the objects might be equal (like an empty map), but we want to create a new separate object anyway in the sub contexts.
13+
* As a result also change the `TwContextAttributeChangeListener` to `TwContextAttributePutListener`.
14+
`TwContextAttributePutListener` is now always called when the `twContext.put()` is called regardless if the value changed or not. This also enables wider use of the interface.
15+
816
## [0.12.2] - 2023-08-03
917

1018
### Bumped

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Many applications have multiple owners - on endpoints, jobs and other units of w
1212

1313
It is useful to correlate Rollbar errors, logs and even metrics by specific owners.
1414

15-
`com.transferwise.common.context.TwContextAttributeChangeListenerTest.ownerCanBeSetWhenNameIsChanged` describes how
15+
`com.transferwise.common.context.TwContextAttributePutListenerTest.ownerCanBeSetWhenNameIsChanged` describes how
1616
to make the application set the owner field.
1717

1818
But for services, to set an owner it is recommended to use `tw-context-ownership-starter` module instead.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=0.12.2
1+
version=1.0.0
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package com.transferwise.common.context.ownership;
22

33
import com.transferwise.common.context.TwContext;
4-
import com.transferwise.common.context.TwContextAttributeChangeListener;
4+
import com.transferwise.common.context.TwContextAttributePutListener;
55
import java.util.LinkedHashMap;
66
import java.util.Map;
7+
import java.util.Objects;
78
import java.util.concurrent.locks.Lock;
89
import java.util.concurrent.locks.ReentrantLock;
910
import lombok.extern.slf4j.Slf4j;
1011
import org.apache.commons.lang3.tuple.Pair;
1112
import org.springframework.beans.factory.annotation.Autowired;
1213

1314
@Slf4j
14-
public class EntryPointOwnerAttributesChangeListener implements TwContextAttributeChangeListener {
15+
public class EntryPointOwnerAttributesPutListener implements TwContextAttributePutListener {
1516

1617
@Autowired
1718
private EntryPointOwnerProviderRegistry entryPointOwnerProviderRegistry;
@@ -31,7 +32,10 @@ public boolean removeEldestEntry(Map.Entry<Pair<String, String>, Boolean> eldest
3132
private final Lock defaultOwnersLock = new ReentrantLock();
3233

3334
@Override
34-
public void attributeChanged(TwContext context, String key, Object oldValue, Object newValue) {
35+
public void attributePut(TwContext context, String key, Object oldValue, Object newValue) {
36+
if (Objects.equals(oldValue, newValue)) {
37+
return;
38+
}
3539
if (TwContext.NAME_KEY.equals(key)) {
3640
String epGroup = context.getGroup();
3741
String epName = context.getName();

tw-context-ownership-starter/src/main/java/com/transferwise/common/context/ownership/TwContextOwnershipAutoConfiguration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.transferwise.common.context.ownership;
22

33
import com.transferwise.common.context.TwContext;
4-
import com.transferwise.common.context.TwContextAttributeChangeListener;
4+
import com.transferwise.common.context.TwContextAttributePutListener;
55
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
66
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
77
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -18,9 +18,9 @@ public EntryPointOwnerProviderRegistry entryPointOwnerProviderRegistry() {
1818
}
1919

2020
@Bean
21-
public TwContextAttributeChangeListener twContextOwnershipAttributesChangeListener() {
22-
EntryPointOwnerAttributesChangeListener listener = new EntryPointOwnerAttributesChangeListener();
23-
TwContext.addAttributeChangeListener(listener);
21+
public TwContextAttributePutListener twContextOwnershipAttributesPutListener() {
22+
EntryPointOwnerAttributesPutListener listener = new EntryPointOwnerAttributesPutListener();
23+
TwContext.addAttributePutListener(listener);
2424
return listener;
2525
}
2626

tw-context-ownership-starter/src/test/java/com/transferwise/common/context/ownership/OwnershipIntTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
public class OwnershipIntTest {
1818

1919
@Autowired
20-
private EntryPointOwnerAttributesChangeListener entryPointOwnerAttributesChangeListener;
20+
private EntryPointOwnerAttributesPutListener entryPointOwnerAttributesPutListener;
2121

2222
@Test
2323
public void ownerShipIsMappedByConfiguration() {
@@ -33,11 +33,11 @@ public void ownerShipIsMappedByConfiguration() {
3333

3434
@Test
3535
void entrypointsWithoutOwnerShouldBeLoggedOnce() {
36-
entryPointOwnerAttributesChangeListener.clearDefaultOwners();
36+
entryPointOwnerAttributesPutListener.clearDefaultOwners();
3737

3838
TwContext twContext = TwContext.current().createSubContext().asEntryPoint("Jobs", "testJob1");
3939

40-
Logger logger = (Logger) LoggerFactory.getLogger(EntryPointOwnerAttributesChangeListener.class);
40+
Logger logger = (Logger) LoggerFactory.getLogger(EntryPointOwnerAttributesPutListener.class);
4141
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
4242
listAppender.start();
4343
logger.addAppender(listAppender);

tw-context/src/main/java/com/transferwise/common/context/TwContext.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import java.util.Iterator;
88
import java.util.List;
99
import java.util.Map;
10-
import java.util.Objects;
1110
import java.util.Optional;
1211
import java.util.concurrent.CopyOnWriteArrayList;
1312
import java.util.function.Function;
@@ -35,7 +34,7 @@ public class TwContext {
3534

3635
private static final ThreadLocal<Optional<TwContext>> contextTl = new ThreadLocal<>();
3736
private static final List<TwContextExecutionInterceptor> interceptors = new CopyOnWriteArrayList<>();
38-
private static final List<TwContextAttributeChangeListener> attributeChangeListeners = new CopyOnWriteArrayList<>();
37+
private static final List<TwContextAttributePutListener> attributePutListeners = new CopyOnWriteArrayList<>();
3938
private static final TwContext ROOT_CONTEXT = new TwContext(null, true);
4039
private static final RateLimiter throwableLoggingRateLimiter = RateLimiter.create(2);
4140

@@ -57,17 +56,17 @@ public static List<TwContextExecutionInterceptor> getExecutionInterceptors() {
5756
return interceptors;
5857
}
5958

60-
public static void addAttributeChangeListener(@NonNull TwContextAttributeChangeListener listener) {
61-
attributeChangeListeners.add(listener);
59+
public static void addAttributePutListener(@NonNull TwContextAttributePutListener listener) {
60+
attributePutListeners.add(listener);
6261
}
6362

64-
public static boolean removeAttributeChangeListener(TwContextAttributeChangeListener listener) {
65-
return attributeChangeListeners.remove(listener);
63+
public static boolean removeAttributePutListener(TwContextAttributePutListener listener) {
64+
return attributePutListeners.remove(listener);
6665
}
6766

6867
@SuppressFBWarnings(value = "MS", justification = "Performance")
69-
public static List<TwContextAttributeChangeListener> getAttributeChangeListeners() {
70-
return attributeChangeListeners;
68+
public static List<TwContextAttributePutListener> getAttributePutListeners() {
69+
return attributePutListeners;
7170
}
7271

7372
public static void putCurrentMdc(@NonNull String key, String value) {
@@ -204,17 +203,16 @@ public TwContext put(String key, Object newValue) {
204203
}
205204
Object oldValue = attributes.get(key);
206205

207-
if (!Objects.equals(oldValue, newValue)) {
208-
if (newValue == null) {
209-
attributes.remove(key);
210-
newAttributes.remove(key);
211-
} else {
212-
attributes.put(key, newValue);
213-
newAttributes.put(key, newValue);
214-
}
215-
fireAttributeChangeEvent(key, oldValue, newValue);
206+
if (newValue == null) {
207+
attributes.remove(key);
208+
newAttributes.remove(key);
209+
} else {
210+
attributes.put(key, newValue);
211+
newAttributes.put(key, newValue);
216212
}
217213

214+
fireAttributePutEvent(key, oldValue, newValue);
215+
218216
return this;
219217
}
220218

@@ -306,15 +304,15 @@ private <T> T executeWithInterceptors(Supplier<T> supplier, Iterator<TwContextEx
306304
return supplier.get();
307305
}
308306

309-
private void fireAttributeChangeEvent(String key, Object oldValue, Object newValue) {
310-
if (attributeChangeListeners != null) {
307+
private void fireAttributePutEvent(String key, Object oldValue, Object newValue) {
308+
if (attributePutListeners != null) {
311309
try {
312-
attributeChangeListeners.forEach((l) -> l.attributeChanged(this, key, oldValue, newValue));
310+
attributePutListeners.forEach((l) -> l.attributePut(this, key, oldValue, newValue));
313311
} catch (Throwable t) {
314312
// This is just a safety net, every listener needs to be bullet-proof by themselves.
315313
if (throwableLoggingRateLimiter.tryAcquire()) {
316314
// Don't log value, could be PII.
317-
log.error("Attribute change listener failed for key '" + key + "'.", t);
315+
log.error("Attribute put listener failed for key '" + key + "'.", t);
318316
}
319317
}
320318
}

tw-context/src/main/java/com/transferwise/common/context/TwContextAttributeChangeListener.java

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.transferwise.common.context;
2+
3+
import javax.annotation.concurrent.NotThreadSafe;
4+
5+
@NotThreadSafe
6+
public interface TwContextAttributePutListener {
7+
8+
void attributePut(TwContext context, String key, Object oldValue, Object newValue);
9+
}
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import org.junit.jupiter.api.Test;
77
import org.slf4j.MDC;
88

9-
public class TwContextAttributeChangeListenerTest {
9+
public class TwContextAttributePutListenerTest {
1010

1111
@Test
1212
void nameAttributeIsChangedAfterGroupAttribute() {
1313
MutableObject<Boolean> groupChanged = new MutableObject<>(false);
1414
MutableObject<Boolean> nameChanged = new MutableObject<>(false);
15-
TwContextAttributeChangeListener listener = (context, key, oldValue, newValue) -> {
15+
TwContextAttributePutListener listener = (context, key, oldValue, newValue) -> {
1616
if (TwContext.GROUP_KEY.equals(key)) {
1717
groupChanged.setValue(true);
1818
} else if (TwContext.NAME_KEY.equals(key)) {
@@ -22,20 +22,20 @@ void nameAttributeIsChangedAfterGroupAttribute() {
2222
nameChanged.setValue(true);
2323
}
2424
};
25-
TwContext.addAttributeChangeListener(listener);
25+
TwContext.addAttributePutListener(listener);
2626
try {
2727
TwContext.current().createSubContext().asEntryPoint("SRE", "Task123");
2828

2929
assertThat(nameChanged.getValue()).isTrue();
3030
assertThat(groupChanged.getValue()).isTrue();
3131
} finally {
32-
TwContext.removeAttributeChangeListener(listener);
32+
TwContext.removeAttributePutListener(listener);
3333
}
3434
}
3535

3636
@Test
3737
void ownerCanBeSetWhenNameIsChanged() {
38-
TwContextAttributeChangeListener listener = (context, key, oldValue, newValue) -> {
38+
TwContextAttributePutListener listener = (context, key, oldValue, newValue) -> {
3939
// Here we are making an assumption (covered by test in TwContext), that the name key is always changed after group key.
4040
// We could remove that assumption by deciding owner both on group key change and on name key change, but that would incur double work.
4141
if (TwContext.NAME_KEY.equals(key)) {
@@ -47,7 +47,7 @@ void ownerCanBeSetWhenNameIsChanged() {
4747
}
4848
};
4949

50-
TwContext.addAttributeChangeListener(listener);
50+
TwContext.addAttributePutListener(listener);
5151
try {
5252
TwContext twContext = TwContext.current().createSubContext().asEntryPoint("Test", "/ep1");
5353
assertThat(twContext.getOwner()).isEqualTo("Kristo");
@@ -59,7 +59,7 @@ void ownerCanBeSetWhenNameIsChanged() {
5959
assertThat(MDC.get(TwContext.MDC_KEY_EP_OWNER)).isEqualTo("Yurii");
6060
});
6161
} finally {
62-
TwContext.removeAttributeChangeListener(listener);
62+
TwContext.removeAttributePutListener(listener);
6363
}
6464
}
6565
}

0 commit comments

Comments
 (0)