Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changes/en-us/2.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Add changes here for all PR submitted to the 2.x branch.

### feature:

- [[#8140](https://github.com/apache/incubator-seata/pull/8140)] support automatic updated marking after BusinessActionContext modifications

### bugfix:

Expand All @@ -44,6 +45,7 @@ Thanks to these contributors for their code commits. Please report an unintended
<!-- Please make sure your Github ID is in the list below -->

- [slievrly](https://github.com/slievrly)
- [Zhengcy05](https://github.com/Zhengcy05)


Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.
2 changes: 2 additions & 0 deletions changes/zh-cn/2.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

### feature:

- [[#8140](https://github.com/apache/incubator-seata/pull/8140)] 支持在 BusinessActionContext 变更后自动标记 updated

### bugfix:

Expand All @@ -44,6 +45,7 @@
<!-- 请确保您的 GitHub ID 在以下列表中 -->

- [slievrly](https://github.com/slievrly)
- [Zhengcy05](https://github.com/Zhengcy05)


同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public Object proceed(
// MDC put branchId
MDC.put(RootContext.MDC_KEY_BRANCH_ID, branchId);

// enable mutation tracking only after framework initialization is complete
actionContext.enableActionContextTracking();

// save the previous action context
BusinessActionContext previousActionContext = BusinessActionContextUtil.getContext();
try {
Expand Down Expand Up @@ -232,10 +235,13 @@ protected String doTxActionLogStore(

Map<String, Object> originContext = actionContext.getActionContext();
if (CollectionUtils.isNotEmpty(originContext)) {
// Merge context and origin context if it exists.
// @since: above 1.4.2
originContext.putAll(context);
context = originContext;
// Keep framework-side merge outside the tracked map to avoid false updated flags.
// Merge framework context into a fresh map to avoid treating framework-side
// initialization as a business mutation when tracking is already enabled.
Map<String, Object> mergedContext = new HashMap<>(originContext);
mergedContext.putAll(context);
actionContext.setActionContext(mergedContext);
context = mergedContext;
} else {
actionContext.setActionContext(context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

/**
* The type Business action context.
Expand Down Expand Up @@ -62,6 +69,11 @@ public class BusinessActionContext implements Serializable {
*/
private Map<String, Object> actionContext;

/**
* whether the action context should stay tracked on mutation
*/
private transient boolean actionContextTrackingEnabled;

/**
* Instantiates a new Business action context.
*/
Expand Down Expand Up @@ -147,9 +159,27 @@ public Map<String, Object> getActionContext() {
* @param actionContext the action context
*/
public void setActionContext(Map<String, Object> actionContext) {
if (actionContextTrackingEnabled) {
this.actionContext = actionContext == null
? new TrackedActionContextMap(this)
: new TrackedActionContextMap(this, actionContext);
return;
}
this.actionContext = actionContext;
}

/**
* Enable automatic updated tracking for action context mutations.
*/
public void enableActionContextTracking() {
actionContextTrackingEnabled = true;
if (actionContext == null) {
actionContext = new TrackedActionContextMap(this);
} else if (!(actionContext instanceof TrackedActionContextMap)) {
actionContext = new TrackedActionContextMap(this, actionContext);
}
}

/**
* Gets xid.
*
Expand Down Expand Up @@ -233,6 +263,10 @@ public void setBranchType(BranchType branchType) {
this.branchType = branchType;
}

private void markUpdatedOnActionContextMutation() {
setUpdated(true);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Expand All @@ -253,4 +287,173 @@ public String toString() {
.append("]");
return sb.toString();
}

/**
* The tracked action context map.
*/
private static final class TrackedActionContextMap extends AbstractMap<String, Object> implements Serializable {

private static final long serialVersionUID = 1L;

private final BusinessActionContext owner;

private final Map<String, Object> delegate;

private TrackedActionContextMap(BusinessActionContext owner) {
this.owner = owner;
this.delegate = new HashMap<>(8);
}

private TrackedActionContextMap(BusinessActionContext owner, Map<String, Object> source) {
this.owner = owner;
this.delegate = new HashMap<>(source);
}

@Override
public Object put(String key, Object value) {
boolean hadKey = delegate.containsKey(key);
Object previousValue = delegate.put(key, value);
if (!hadKey || !Objects.equals(previousValue, value)) {
owner.markUpdatedOnActionContextMutation();
}
return previousValue;
}

@Override
public void putAll(Map<? extends String, ? extends Object> m) {
Objects.requireNonNull(m, "m");
if (m.isEmpty()) {
return;
}
for (Map.Entry<? extends String, ? extends Object> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}

@Override
public Object remove(Object key) {
boolean hadKey = delegate.containsKey(key);
Object previousValue = delegate.remove(key);
if (hadKey) {
owner.markUpdatedOnActionContextMutation();
}
return previousValue;
}

@Override
public void clear() {
if (!delegate.isEmpty()) {
delegate.clear();
owner.markUpdatedOnActionContextMutation();
}
}

@Override
public Set<Entry<String, Object>> entrySet() {
return new AbstractSet<Entry<String, Object>>() {
@Override
public Iterator<Entry<String, Object>> iterator() {
Iterator<Entry<String, Object>> iterator =
delegate.entrySet().iterator();
return new Iterator<Entry<String, Object>>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}

@Override
public Entry<String, Object> next() {
Entry<String, Object> current = iterator.next();
return new TrackingEntry(current);
}

@Override
public void remove() {
iterator.remove();
owner.markUpdatedOnActionContextMutation();
}
};
}

// The following methods delegate directly to the underlying Map.
@Override
public int size() {
return delegate.size();
}

@Override
public boolean remove(Object o) {
if (!(o instanceof Entry)) {
return false;
}
Entry<?, ?> entry = (Entry<?, ?>) o;
if (!delegate.containsKey(entry.getKey())) {
return false;
}
if (!Objects.equals(delegate.get(entry.getKey()), entry.getValue())) {
return false;
}
TrackedActionContextMap.this.remove(entry.getKey());
return true;
}
};
}

@Override
public int size() {
return delegate.size();
}

@Override
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}

@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}

@Override
public Object get(Object key) {
return delegate.get(key);
}

private final class TrackingEntry implements Entry<String, Object> {
private final Entry<String, Object> delegateEntry;

private TrackingEntry(Entry<String, Object> delegateEntry) {
this.delegateEntry = delegateEntry;
}

@Override
public String getKey() {
return delegateEntry.getKey();
}

@Override
public Object getValue() {
return delegateEntry.getValue();
}

@Override
public Object setValue(Object value) {
Object previousValue = delegateEntry.setValue(value);
if (!Objects.equals(previousValue, value)) {
owner.markUpdatedOnActionContextMutation();
}
return previousValue;
}

@Override
public boolean equals(Object o) {
return delegateEntry.equals(o);
}

@Override
public int hashCode() {
return delegateEntry.hashCode();
}
}
}
}
Loading
Loading