From 719563c1ba6744a45eb400e39f410ecac65c4612 Mon Sep 17 00:00:00 2001 From: "ye.zou" Date: Fri, 13 Feb 2026 10:59:09 +0800 Subject: [PATCH] [portForwarding]: serialize concurrent port forwarding rule creation per VIP to prevent duplicate rules Resolves: ZSTAC-77673 Change-Id: I7f03df7bd22cd7d39097a34197313126bea811e1 --- .../PortForwardingManagerImpl.java | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/plugin/portForwarding/src/main/java/org/zstack/network/service/portforwarding/PortForwardingManagerImpl.java b/plugin/portForwarding/src/main/java/org/zstack/network/service/portforwarding/PortForwardingManagerImpl.java index ce34e098021..50523d3b214 100755 --- a/plugin/portForwarding/src/main/java/org/zstack/network/service/portforwarding/PortForwardingManagerImpl.java +++ b/plugin/portForwarding/src/main/java/org/zstack/network/service/portforwarding/PortForwardingManagerImpl.java @@ -682,10 +682,34 @@ public void fail(ErrorCode errorCode) { private void handle(APICreatePortForwardingRuleMsg msg) { final APICreatePortForwardingRuleEvent evt = new APICreatePortForwardingRuleEvent(msg.getId()); - int vipPortEnd = msg.getVipPortEnd() == null ? msg.getVipPortStart() : msg.getVipPortEnd(); - int privatePortEnd = msg.getPrivatePortEnd() == null ? msg.getPrivatePortStart() : msg.getPrivatePortEnd(); + thdf.chainSubmit(new ChainTask(msg) { + @Override + public String getSyncSignature() { + return String.format("portforwardingrule-vip-%s", msg.getVipUuid()); + } - VipVO vip = dbf.findByUuid(msg.getVipUuid(), VipVO.class); + @Override + public void run(SyncTaskChain chain) { + int vipPortEnd = msg.getVipPortEnd() == null ? msg.getVipPortStart() : msg.getVipPortEnd(); + int privatePortEnd = msg.getPrivatePortEnd() == null ? msg.getPrivatePortStart() : msg.getPrivatePortEnd(); + + // re-check VIP port overlap under sync to prevent concurrent duplicate rules + boolean overlap = Q.New(PortForwardingRuleVO.class) + .eq(PortForwardingRuleVO_.vipUuid, msg.getVipUuid()) + .eq(PortForwardingRuleVO_.protocolType, PortForwardingProtocolType.valueOf(msg.getProtocolType())) + .lte(PortForwardingRuleVO_.vipPortStart, vipPortEnd) + .gte(PortForwardingRuleVO_.vipPortEnd, msg.getVipPortStart()) + .isExists(); + if (overlap) { + evt.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_PORTFORWARDING_10017, + "vip port range[vipStartPort:%s, vipEndPort:%s] overlaps with an existing port forwarding rule on vip[uuid:%s]", + msg.getVipPortStart(), vipPortEnd, msg.getVipUuid())); + bus.publish(evt); + chain.next(); + return; + } + + VipVO vip = dbf.findByUuid(msg.getVipUuid(), VipVO.class); final PortForwardingRuleVO vo = new PortForwardingRuleVO(); if (msg.getResourceUuid() != null) { vo.setUuid(msg.getResourceUuid()); @@ -713,8 +737,8 @@ protected void scripts() { } }.execute(); - FlowChain chain = FlowChainBuilder.newShareFlowChain(); - chain.setName("create-portforwading"); + FlowChain flowChain = FlowChainBuilder.newShareFlowChain(); + flowChain.setName("create-portforwading"); VipInventory vipInventory = VipInventory.valueOf(vip); if (msg.getVmNicUuid() == null) { ModifyVipAttributesStruct struct = new ModifyVipAttributesStruct(); @@ -727,6 +751,7 @@ protected void scripts() { public void success() { evt.setInventory(PortForwardingRuleInventory.valueOf(vo)); bus.publish(evt); + chain.next(); } @Override @@ -734,6 +759,7 @@ public void fail(ErrorCode errorCode) { dbf.remove(vo); evt.setError(errorCode); bus.publish(evt); + chain.next(); } }); @@ -757,6 +783,7 @@ public void fail(ErrorCode errorCode) { public void success() { evt.setInventory(PortForwardingRuleInventory.valueOf(vo)); bus.publish(evt); + chain.next(); } @Override @@ -764,13 +791,14 @@ public void fail(ErrorCode errorCode) { dbf.remove(vo); evt.setError(errorCode); bus.publish(evt); + chain.next(); } }); return; } - chain.then(new ShareFlow() { + flowChain.then(new ShareFlow() { @Override public void setup() { vo.setVmNicUuid(vmNic.getUuid()); @@ -853,11 +881,12 @@ public void fail(ErrorCode errorCode) { }); - chain.done(new FlowDoneHandler(msg) { + flowChain.done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { evt.setInventory(PortForwardingRuleInventory.valueOf(dbf.reload(vo))); bus.publish(evt); + chain.next(); } }).error(new FlowErrorHandler(msg) { @Override @@ -865,8 +894,16 @@ public void handle(ErrorCode errCode, Map data) { dbf.remove(vo); evt.setError(errCode); bus.publish(evt); + chain.next(); } }).start(); + } + + @Override + public String getName() { + return String.format("api-create-portforwardingrule-vip-%s", msg.getVipUuid()); + } + }); } private void populateExtensions() {