Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2223301/?format=api
{ "id": 2223301, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2223301/?format=api", "web_url": "http://patchwork.ozlabs.org/project/ovn/patch/20260414232958.189990-1-tiago.reis@luizalabs.com/", "project": { "id": 68, "url": "http://patchwork.ozlabs.org/api/1.1/projects/68/?format=api", "name": "Open Virtual Network development", "link_name": "ovn", "list_id": "ovs-dev.openvswitch.org", "list_email": "ovs-dev@openvswitch.org", "web_url": "http://openvswitch.org/", "scm_url": "", "webscm_url": "" }, "msgid": "<20260414232958.189990-1-tiago.reis@luizalabs.com>", "date": "2026-04-14T23:29:58", "name": "[ovs-dev,v4] ovn-ic: Use dual IC-SB connections to prevent constraint violations.", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "072a26772e5bbd4b3bf724da59c20cfccf3331dd", "submitter": { "id": 92676, "url": "http://patchwork.ozlabs.org/api/1.1/people/92676/?format=api", "name": "Tiago Matos", "email": "tiago.reis@luizalabs.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20260414232958.189990-1-tiago.reis@luizalabs.com/mbox/", "series": [ { "id": 499901, "url": "http://patchwork.ozlabs.org/api/1.1/series/499901/?format=api", "web_url": "http://patchwork.ozlabs.org/project/ovn/list/?series=499901", "date": "2026-04-14T23:29:58", "name": "[ovs-dev,v4] ovn-ic: Use dual IC-SB connections to prevent constraint violations.", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/499901/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2223301/comments/", "check": "warning", "checks": "http://patchwork.ozlabs.org/api/patches/2223301/checks/", "tags": {}, "headers": { "Return-Path": "<ovs-dev-bounces@openvswitch.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "dev@openvswitch.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "ovs-dev@lists.linuxfoundation.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=BHa+D88z;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)", "smtp3.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key,\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=BHa+D88z", "smtp1.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=luizalabs.com", "smtp1.osuosl.org; dkim=pass (1024-bit key,\n unprotected) header.d=luizalabs.com header.i=@luizalabs.com\n header.a=rsa-sha256 header.s=google header.b=BHa+D88z" ], "Received": [ "from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fwL9b3T86z1yHc\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 15 Apr 2026 09:30:25 +1000 (AEST)", "from localhost (localhost [127.0.0.1])\n\tby smtp3.osuosl.org (Postfix) with ESMTP id D35106080A;\n\tTue, 14 Apr 2026 23:30:22 +0000 (UTC)", "from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id SkI3G-MVzig1; Tue, 14 Apr 2026 23:30:20 +0000 (UTC)", "from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp3.osuosl.org (Postfix) with ESMTPS id B92B3607E4;\n\tTue, 14 Apr 2026 23:30:20 +0000 (UTC)", "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 91093C054A;\n\tTue, 14 Apr 2026 23:30:20 +0000 (UTC)", "from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 15AF6C0549\n for <dev@openvswitch.org>; Tue, 14 Apr 2026 23:30:19 +0000 (UTC)", "from localhost (localhost [127.0.0.1])\n by smtp1.osuosl.org (Postfix) with ESMTP id EA73482F31\n for <dev@openvswitch.org>; Tue, 14 Apr 2026 23:30:18 +0000 (UTC)", "from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id 4iczOZAryJuR for <dev@openvswitch.org>;\n Tue, 14 Apr 2026 23:30:17 +0000 (UTC)", "from mail-vk1-xa42.google.com (mail-vk1-xa42.google.com\n [IPv6:2607:f8b0:4864:20::a42])\n by smtp1.osuosl.org (Postfix) with ESMTPS id 5224880BD3\n for <dev@openvswitch.org>; Tue, 14 Apr 2026 23:30:17 +0000 (UTC)", "by mail-vk1-xa42.google.com with SMTP id\n 71dfb90a1353d-56a86f0a23bso6217436e0c.0\n for <dev@openvswitch.org>; Tue, 14 Apr 2026 16:30:17 -0700 (PDT)", "from state ([2804:10dc:d35d:f00:3256:fff:fe0d:5e27])\n by smtp.gmail.com with ESMTPSA id\n 71dfb90a1353d-56f89fa7bf7sm107015e0c.12.2026.04.14.16.30.13\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 14 Apr 2026 16:30:14 -0700 (PDT)" ], "X-Virus-Scanned": [ "amavis at osuosl.org", "amavis at osuosl.org" ], "X-Comment": "SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ", "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 smtp3.osuosl.org B92B3607E4", "OpenDKIM Filter v2.11.0 smtp1.osuosl.org 5224880BD3" ], "Received-SPF": "Pass (mailfrom) identity=mailfrom;\n client-ip=2607:f8b0:4864:20::a42; helo=mail-vk1-xa42.google.com;\n envelope-from=tiago.reis@luizalabs.com; receiver=<UNKNOWN>", "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp1.osuosl.org 5224880BD3", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=luizalabs.com; s=google; t=1776209416; x=1776814216; darn=openvswitch.org;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=p05KmroYhce3cn72zzAwtjaRQI2afV1aKROP3xtnyNo=;\n b=BHa+D88zV5Qf7NxRtZ0adRsrLKrqHC9Wg6t7EEZzSUT7cYRJRt5CnO0lt6IiL0Q2+h\n L1fkMrjGtZBfOeNYCvZaNz5w+2HpA1nn9jr0RgjN3+sg6R5gEY+cFBM4mWp+mBoF6IV0\n LRbVEEPBaj3oX0wAA8P7WjCdbSNkwMFC6sVwQ=", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776209416; x=1776814216;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=p05KmroYhce3cn72zzAwtjaRQI2afV1aKROP3xtnyNo=;\n b=UDL1u9VqhrE8jCnMk+v7FEycmOZj5ta1I8LcBHXksRswC9wWxJhVO9iHT2uzbT9qS0\n 2GTL7nR1ZyYLudmzW8ol3q8HMP2WGHxdvWvGa8N3SFqsp+dIFkxMcy2cKW9coICgVUdJ\n Fw1FPk8aZ+R5kgddj3H3dHFtzsGZHbCwJKASMDKucEYwxTya6AvA+NuFRIVIIcJHqfyR\n T///2PZn+Yxxo4rlr4qf50NqDND14O2G+COuHzQ2WwEUuDmcUZxBpl9wOur/UZR2sRZD\n woXVnlx+JDClnNNpq0L4Nic4qKEI0vjKvpB7WjZBJeody+zAU83lA2MKm0VICGpf6bf0\n k0YA==", "X-Gm-Message-State": "AOJu0Yw3bdj900A0ChnAHQLduTOdTnsFlJYbK/VVwmEWko3PG5xl/M9s\n eq2FalMbsCuyKS3l0qD5yh3rvxNls1SdIfL32gcK6TCPPX0GRQi7MjkoQydmFeKeQnj9wHMc9sf\n NuB9wakw4eLfBfKQLD4/UN0HDpiSUhY5xC33MRNg5iS1Sfv5LRcAjlxL/5aC43Qwn5BHO", "X-Gm-Gg": "AeBDieswbSlJUONdm09vwG5YMXqnemdAY6nXSkV2Q3Mn+2LLAOsAPrhhDjusxwVy2ow\n RfzBMSyelA7ct2MGgzsW1BivmNwVcd/Mdgjhu0xfReyM/6zhMMscCrUbPQowqq7uNHv+BqGwHlt\n eIBAy4cCs03P8F8vpY4ze61fpYrql9DMXzd7gW5noZezii/RbWatMtlp2VI2CcbQvNeUD13myvF\n 6nPsIE4cDQeJWQex5ysQIDjBvR9srLVlM3nUHzxW7zWl7G0UqPTKZ/lNxXmxniOZj9lqOirOMmL\n r/Xp2YYJasyZEiFDovkJ6iQS9+8fd6DPPzAeCgqZwYX/FAMlOpb+K1fGndocQ/tU9mcdMijpw6D\n 0UGM8uYWdNQXXxVkpTqjdFhhgM29E3TvIhWXurnv/94rl/0n/0Jm660PERQfQrj12NJeU377Umy\n rn7yGs2xHyG1Ce1l+JacMBF5Re8IhtabPyPA==", "X-Received": "by 2002:a05:6122:4594:b0:56f:8f5:b135 with SMTP id\n 71dfb90a1353d-56f3bcf7b11mr9896164e0c.14.1776209415523;\n Tue, 14 Apr 2026 16:30:15 -0700 (PDT)", "To": "dev@openvswitch.org", "Date": "Tue, 14 Apr 2026 20:29:58 -0300", "Message-ID": "<20260414232958.189990-1-tiago.reis@luizalabs.com>", "X-Mailer": "git-send-email 2.53.0", "MIME-Version": "1.0", "Subject": "[ovs-dev] [PATCH ovn v4] ovn-ic: Use dual IC-SB connections to\n prevent constraint violations.", "X-BeenThere": "ovs-dev@openvswitch.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "<ovs-dev.openvswitch.org>", "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>", "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>", "List-Post": "<mailto:ovs-dev@openvswitch.org>", "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>", "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>", "From": "Tiago Matos via dev <ovs-dev@openvswitch.org>", "Reply-To": "Tiago Matos <tiago.reis@luizalabs.com>", "Content-Type": "text/plain; charset=\"iso-8859-1\"", "Content-Transfer-Encoding": "quoted-printable", "Errors-To": "ovs-dev-bounces@openvswitch.org", "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>" }, "content": "When multiple Availability Zones (AZs) are connected via OVN-IC,\ncertain events trigger all AZs to attempt writing the same data to the\nIC-SB simultaneously. This race condition leads to constraint\nviolations, causing transaction failures and forcing expensive full\nrecomputes.\n\nTo mitigate this, this patch introduces a write-segregation mechanism\nemploying two distinct connections to IC-SB:\n1. Locked Connection: Acquires a lock to handle data that only a\n single AZ should write (e.g., creating a new datapath_binding for a\n transit switch/router).\n\n2. Lockless Connections: Used for data that multiple AZs can safely\n insert concurrently (e.g., creating a new port_binding).\n\nSigned-off-by: Tiago Matos <tiago.reis@luizalabs.com>\n---\n ic/inc-proc-ic.c | 6 +-\n ic/inc-proc-ic.h | 1 +\n ic/ovn-ic.c | 197 ++++++++++++++++++++++++++++++++++++-----------\n ic/ovn-ic.h | 2 +\n tests/ovn-ic.at | 44 +++++++++++\n 5 files changed, 201 insertions(+), 49 deletions(-)", "diff": "diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c\nindex 995f23433..2c0420292 100644\n--- a/ic/inc-proc-ic.c\n+++ b/ic/inc-proc-ic.c\n@@ -27,6 +27,7 @@\n #include \"openvswitch/vlog.h\"\n #include \"inc-proc-ic.h\"\n #include \"en-ic.h\"\n+#include \"ovn-util.h\"\n #include \"unixctl.h\"\n #include \"util.h\"\n \n@@ -214,7 +215,7 @@ inc_proc_ic_run(struct ic_context *ctx,\n struct ic_engine_context *ic_eng_ctx)\n {\n ovs_assert(ctx->ovnnb_txn && ctx->ovnsb_txn &&\n- ctx->ovninb_txn && ctx->ovnisb_txn);\n+ ctx->ovninb_txn && ctx->ovnisb_unlocked_txn);\n \n int64_t start = time_msec();\n engine_init_run();\n@@ -262,7 +263,8 @@ inc_proc_ic_can_run(struct ic_engine_context *ctx)\n ctx->nb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||\n ctx->sb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||\n ctx->inb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||\n- ctx->isb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS) {\n+ ctx->isb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||\n+ ctx->isb_unlock_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS) {\n return true;\n }\n \ndiff --git a/ic/inc-proc-ic.h b/ic/inc-proc-ic.h\nindex 9af147fb3..36464564d 100644\n--- a/ic/inc-proc-ic.h\n+++ b/ic/inc-proc-ic.h\n@@ -13,6 +13,7 @@ struct ic_engine_context {\n uint64_t sb_idl_duration_ms;\n uint64_t inb_idl_duration_ms;\n uint64_t isb_idl_duration_ms;\n+ uint64_t isb_unlock_idl_duration_ms;\n uint32_t backoff_ms;\n };\n \ndiff --git a/ic/ovn-ic.c b/ic/ovn-ic.c\nindex 17feeb4af..8b163fbf7 100644\n--- a/ic/ovn-ic.c\n+++ b/ic/ovn-ic.c\n@@ -115,8 +115,9 @@ az_run(struct ic_context *ctx)\n * \"ovn-ic-sbctl destroy avail <az>\". */\n static char *az_name;\n const struct icsbrec_availability_zone *az;\n- if (ctx->ovnisb_txn && az_name && strcmp(az_name, nb_global->name)) {\n- ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_idl) {\n+ if (ctx->ovnisb_unlocked_txn && az_name\n+ && strcmp(az_name, nb_global->name)) {\n+ ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_unlocked_idl) {\n /* AZ name update locally need to update az in ISB. */\n if (nb_global->name[0] && !strcmp(az->name, az_name)) {\n icsbrec_availability_zone_set_name(az, nb_global->name);\n@@ -138,11 +139,11 @@ az_run(struct ic_context *ctx)\n az_name = xstrdup(nb_global->name);\n }\n \n- if (ctx->ovnisb_txn) {\n- ovsdb_idl_txn_add_comment(ctx->ovnisb_txn, \"AZ %s\", az_name);\n+ if (ctx->ovnisb_unlocked_txn) {\n+ ovsdb_idl_txn_add_comment(ctx->ovnisb_unlocked_txn, \"AZ %s\", az_name);\n }\n \n- ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_idl) {\n+ ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_unlocked_idl) {\n if (!strcmp(az->name, az_name)) {\n ctx->runned_az = az;\n return az;\n@@ -150,9 +151,9 @@ az_run(struct ic_context *ctx)\n }\n \n /* Create AZ in ISB */\n- if (ctx->ovnisb_txn) {\n+ if (ctx->ovnisb_unlocked_txn) {\n VLOG_INFO(\"Register AZ %s to interconnection DB.\", az_name);\n- az = icsbrec_availability_zone_insert(ctx->ovnisb_txn);\n+ az = icsbrec_availability_zone_insert(ctx->ovnisb_unlocked_txn);\n icsbrec_availability_zone_set_name(az, az_name);\n ctx->runned_az = az;\n return az;\n@@ -195,7 +196,7 @@ enumerate_datapaths(struct ic_context *ctx, struct hmap *dp_tnlids,\n struct shash *isb_ts_dps, struct shash *isb_tr_dps)\n {\n const struct icsbrec_datapath_binding *isb_dp;\n- ICSBREC_DATAPATH_BINDING_FOR_EACH (isb_dp, ctx->ovnisb_idl) {\n+ ICSBREC_DATAPATH_BINDING_FOR_EACH (isb_dp, ctx->ovnisb_unlocked_idl) {\n ovn_add_tnlid(dp_tnlids, isb_dp->tunnel_key);\n \n enum ic_datapath_type dp_type = ic_dp_get_type(isb_dp);\n@@ -209,6 +210,16 @@ enumerate_datapaths(struct ic_context *ctx, struct hmap *dp_tnlids,\n }\n }\n \n+/*\n+ * Check if the AZ is the leader by checking the lock.\n+ */\n+static bool\n+is_az_leader(struct ovsdb_idl_txn *txn)\n+{\n+ struct ovsdb_idl *idl = ovsdb_idl_txn_get_idl(txn);\n+ return idl && ovsdb_idl_has_lock(idl);\n+}\n+\n static void\n ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,\n struct shash *isb_ts_dps)\n@@ -219,9 +230,14 @@ ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,\n const struct icnbrec_ic_nb_global *ic_nb =\n icnbrec_ic_nb_global_first(ctx->ovninb_idl);\n \n+ /*\n+ * Warning: ovnisb_unlocked should not be used to insert data on IC_SB which\n+ * can cause a constraint violation, as an example, inserting data to\n+ * IC-SB datapath_binding.\n+ */\n if (ic_nb && smap_get_bool(&ic_nb->options, \"vxlan_mode\", false)) {\n const struct icsbrec_encap *encap;\n- ICSBREC_ENCAP_FOR_EACH (encap, ctx->ovnisb_idl) {\n+ ICSBREC_ENCAP_FOR_EACH (encap, ctx->ovnisb_unlocked_idl) {\n if (!strcmp(encap->type, \"vxlan\")) {\n vxlan_mode = true;\n break;\n@@ -294,7 +310,8 @@ ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,\n /* Sync TS between INB and ISB. This is performed after syncing with AZ\n * SB, to avoid uncommitted ISB datapath tunnel key to be synced back to\n * AZ. */\n- if (ctx->ovnisb_txn) {\n+ if (ctx->ovnisb_txn &&\n+ is_az_leader(ctx->ovnisb_txn)) {\n /* Create ISB Datapath_Binding */\n ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) {\n const struct icsbrec_datapath_binding *isb_dp =\n@@ -340,6 +357,11 @@ static void\n tr_run(struct ic_context *ctx, struct hmap *dp_tnlids,\n struct shash *isb_tr_dps)\n {\n+ /*\n+ * Warning: ovnisb_unlocked should not be used to insert data on IC_SB which\n+ * can cause a constraint violation, as an example, inserting data to\n+ * IC-SB datapath_binding.\n+ */\n const struct nbrec_logical_router *lr;\n \n if (ctx->ovnnb_txn) {\n@@ -383,7 +405,8 @@ tr_run(struct ic_context *ctx, struct hmap *dp_tnlids,\n /* Sync TR between INB and ISB. This is performed after syncing with AZ\n * SB, to avoid uncommitted ISB datapath tunnel key to be synced back to\n * AZ. */\n- if (ctx->ovnisb_txn) {\n+ if (ctx->ovnisb_txn &&\n+ is_az_leader(ctx->ovnisb_txn)) {\n /* Create ISB Datapath_Binding */\n const struct icnbrec_transit_router *tr;\n ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->ovninb_idl) {\n@@ -488,7 +511,7 @@ sync_sb_gw_to_isb(struct ic_context *ctx,\n struct icsbrec_encap **isb_encaps =\n xmalloc(chassis->n_encaps * sizeof *isb_encaps);\n for (int i = 0; i < chassis->n_encaps; i++) {\n- isb_encap = icsbrec_encap_insert(ctx->ovnisb_txn);\n+ isb_encap = icsbrec_encap_insert(ctx->ovnisb_unlocked_txn);\n icsbrec_encap_set_gateway_name(isb_encap,\n chassis->name);\n icsbrec_encap_set_ip(isb_encap, chassis->encaps[i]->ip);\n@@ -506,14 +529,14 @@ sync_sb_gw_to_isb(struct ic_context *ctx,\n static void\n gateway_run(struct ic_context *ctx)\n {\n- if (!ctx->ovnisb_txn || !ctx->ovnsb_txn) {\n+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {\n return;\n }\n \n struct shash local_gws = SHASH_INITIALIZER(&local_gws);\n struct shash remote_gws = SHASH_INITIALIZER(&remote_gws);\n const struct icsbrec_gateway *gw;\n- ICSBREC_GATEWAY_FOR_EACH (gw, ctx->ovnisb_idl) {\n+ ICSBREC_GATEWAY_FOR_EACH (gw, ctx->ovnisb_unlocked_idl) {\n if (gw->availability_zone == ctx->runned_az) {\n shash_add(&local_gws, gw->name, gw);\n } else {\n@@ -526,7 +549,7 @@ gateway_run(struct ic_context *ctx)\n if (smap_get_bool(&chassis->other_config, \"is-interconn\", false)) {\n gw = shash_find_and_delete(&local_gws, chassis->name);\n if (!gw) {\n- gw = icsbrec_gateway_insert(ctx->ovnisb_txn);\n+ gw = icsbrec_gateway_insert(ctx->ovnisb_unlocked_txn);\n icsbrec_gateway_set_availability_zone(gw, ctx->runned_az);\n icsbrec_gateway_set_name(gw, chassis->name);\n sync_sb_gw_to_isb(ctx, chassis, gw);\n@@ -980,7 +1003,7 @@ create_isb_pb(struct ic_context *ctx, const char *logical_port,\n }\n \n const struct icsbrec_port_binding *isb_pb =\n- icsbrec_port_binding_insert(ctx->ovnisb_txn);\n+ icsbrec_port_binding_insert(ctx->ovnisb_unlocked_txn);\n icsbrec_port_binding_set_availability_zone(isb_pb, az);\n icsbrec_port_binding_set_transit_switch(isb_pb, ts_name);\n icsbrec_port_binding_set_logical_port(isb_pb, logical_port);\n@@ -1068,7 +1091,7 @@ find_lsp_in_sb(struct ic_context *ctx,\n static void\n port_binding_run(struct ic_context *ctx)\n {\n- if (!ctx->ovnisb_txn || !ctx->ovnnb_txn || !ctx->ovnsb_txn) {\n+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnnb_txn || !ctx->ovnsb_txn) {\n return;\n }\n \n@@ -2256,7 +2279,7 @@ advertise_routes(struct ic_context *ctx,\n const char *ts_name,\n struct hmap *routes_ad)\n {\n- ovs_assert(ctx->ovnisb_txn);\n+ ovs_assert(ctx->ovnisb_unlocked_txn);\n const struct icsbrec_route *isb_route;\n const struct icsbrec_route *isb_route_key =\n icsbrec_route_index_init_row(ctx->icsbrec_route_by_ts_az);\n@@ -2298,7 +2321,7 @@ advertise_routes(struct ic_context *ctx,\n /* Create the missing routes in IC-SB */\n struct ic_route_info *route_adv;\n HMAP_FOR_EACH_SAFE (route_adv, node, routes_ad) {\n- isb_route = icsbrec_route_insert(ctx->ovnisb_txn);\n+ isb_route = icsbrec_route_insert(ctx->ovnisb_unlocked_txn);\n icsbrec_route_set_transit_switch(isb_route, ts_name);\n icsbrec_route_set_availability_zone(isb_route, az);\n \n@@ -2531,7 +2554,7 @@ delete_orphan_ic_routes(struct ic_context *ctx,\n static void\n route_run(struct ic_context *ctx)\n {\n- if (!ctx->ovnisb_txn || !ctx->ovnnb_txn || !ctx->ovnsb_txn) {\n+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnnb_txn || !ctx->ovnsb_txn) {\n return;\n }\n \n@@ -2963,7 +2986,7 @@ destroy_service_monitor_data(struct sync_service_monitor_data *sync_data)\n static void\n sync_service_monitor(struct ic_context *ctx)\n {\n- if (!ctx->ovnisb_txn || !ctx->ovnsb_txn) {\n+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {\n return;\n }\n \n@@ -2985,7 +3008,7 @@ sync_service_monitor(struct ic_context *ctx)\n if (ic_rec) {\n sbrec_service_monitor_set_status(db_rec, ic_rec->status);\n } else {\n- ic_rec = icsbrec_service_monitor_insert(ctx->ovnisb_txn);\n+ ic_rec = icsbrec_service_monitor_insert(ctx->ovnisb_unlocked_txn);\n icsbrec_service_monitor_set_type(ic_rec, db_rec->type);\n icsbrec_service_monitor_set_ip(ic_rec, db_rec->ip);\n icsbrec_service_monitor_set_port(ic_rec, db_rec->port);\n@@ -3090,7 +3113,7 @@ static void\n update_sequence_numbers(struct ic_context *ctx,\n struct ovsdb_idl_loop *ic_sb_loop)\n {\n- if (!ctx->ovnisb_txn || !ctx->ovninb_txn) {\n+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovninb_txn) {\n return;\n }\n \n@@ -3100,9 +3123,9 @@ update_sequence_numbers(struct ic_context *ctx,\n ic_nb = icnbrec_ic_nb_global_insert(ctx->ovninb_txn);\n }\n const struct icsbrec_ic_sb_global *ic_sb = icsbrec_ic_sb_global_first(\n- ctx->ovnisb_idl);\n+ ctx->ovnisb_unlocked_idl);\n if (!ic_sb) {\n- ic_sb = icsbrec_ic_sb_global_insert(ctx->ovnisb_txn);\n+ ic_sb = icsbrec_ic_sb_global_insert(ctx->ovnisb_unlocked_txn);\n }\n \n if ((ic_nb->nb_ic_cfg != ic_sb->nb_ic_cfg) &&\n@@ -3112,7 +3135,8 @@ update_sequence_numbers(struct ic_context *ctx,\n icsbrec_availability_zone_set_nb_ic_cfg(ctx->runned_az, 0);\n }\n ic_sb_loop->next_cfg = ic_nb->nb_ic_cfg;\n- ovsdb_idl_txn_increment(ctx->ovnisb_txn, &ctx->runned_az->header_,\n+ ovsdb_idl_txn_increment(ctx->ovnisb_unlocked_txn,\n+ &ctx->runned_az->header_,\n &icsbrec_availability_zone_col_nb_ic_cfg, true);\n return;\n }\n@@ -3127,7 +3151,7 @@ update_sequence_numbers(struct ic_context *ctx,\n }\n \n const struct icsbrec_availability_zone *other_az;\n- ICSBREC_AVAILABILITY_ZONE_FOR_EACH (other_az, ctx->ovnisb_idl) {\n+ ICSBREC_AVAILABILITY_ZONE_FOR_EACH (other_az, ctx->ovnisb_unlocked_idl) {\n if (other_az->nb_ic_cfg != ctx->runned_az->nb_ic_cfg) {\n return;\n }\n@@ -3336,6 +3360,7 @@ static void\n update_idl_probe_interval(struct ovsdb_idl *ovn_sb_idl,\n struct ovsdb_idl *ovn_nb_idl,\n struct ovsdb_idl *ovn_icsb_idl,\n+ struct ovsdb_idl *ovn_icsb_unlocked_idl,\n struct ovsdb_idl *ovn_icnb_idl)\n {\n const struct nbrec_nb_global *nb = nbrec_nb_global_first(ovn_nb_idl);\n@@ -3354,6 +3379,7 @@ update_idl_probe_interval(struct ovsdb_idl *ovn_sb_idl,\n ic_interval);\n }\n set_idl_probe_interval(ovn_icsb_idl, ovn_ic_sb_db, ic_interval);\n+ set_idl_probe_interval(ovn_icsb_unlocked_idl, ovn_ic_sb_db, ic_interval);\n set_idl_probe_interval(ovn_icnb_idl, ovn_ic_nb_db, ic_interval);\n }\n \n@@ -3394,7 +3420,27 @@ main(int argc, char *argv[])\n ovsdb_idl_create(ovn_ic_nb_db, &icnbrec_idl_class, true, true));\n ovsdb_idl_track_add_all(ovninb_idl_loop.idl);\n \n- /* ovn-ic-sb db. */\n+ /*\n+ * Each ovn-ic instance maintains two connections to the IC-SB database:\n+ * 1. Locked Connection: Competes for a global lock on IC-SB. Used for\n+ * writes that must be performed by only one active instance\n+ * (e.g., inserting a datapath_binding for a transit switch/router).\n+ *\n+ * 2. Unlocked Connection: Does not hold a lock. Used for writes that\n+ * can be safely performed by multiple instances simultaneously\n+ * (e.g., inserting a port_binding).\n+ *\n+ * This segregation prevents constraint violations and a full recompute\n+ * when writing to IC-SB.\n+ */\n+ /* ovn-ic-sb db without lock. */\n+ struct ovsdb_idl_loop ovnisb_unlocked_idl_loop =\n+ OVSDB_IDL_LOOP_INITIALIZER(ovsdb_idl_create(ovn_ic_sb_db,\n+ &icsbrec_idl_class,\n+ true, true));\n+ ovsdb_idl_track_add_all(ovnisb_unlocked_idl_loop.idl);\n+\n+ /* ovn-ic-sb db with lock. */\n struct ovsdb_idl_loop ovnisb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(\n ovsdb_idl_create(ovn_ic_sb_db, &icsbrec_idl_class, true, true));\n ovsdb_idl_track_add_all(ovnisb_idl_loop.idl);\n@@ -3619,41 +3665,41 @@ main(int argc, char *argv[])\n &icnbrec_transit_switch_col_name);\n \n struct ovsdb_idl_index *icsbrec_port_binding_by_az\n- = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_port_binding_col_availability_zone);\n \n struct ovsdb_idl_index *icsbrec_port_binding_by_ts\n- = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_port_binding_col_transit_switch);\n \n struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az\n- = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create2(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_port_binding_col_transit_switch,\n &icsbrec_port_binding_col_availability_zone);\n \n struct ovsdb_idl_index *icsbrec_route_by_az\n- = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_route_col_availability_zone);\n \n struct ovsdb_idl_index *icsbrec_route_by_ts\n- = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_route_col_transit_switch);\n \n struct ovsdb_idl_index *icsbrec_route_by_ts_az\n- = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create2(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_route_col_transit_switch,\n &icsbrec_route_col_availability_zone);\n \n struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az\n- = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_service_monitor_col_source_availability_zone);\n \n struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az\n- = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create1(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_service_monitor_col_target_availability_zone);\n \n struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az_logical_port\n- = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,\n+ = ovsdb_idl_index_create2(ovnisb_unlocked_idl_loop.idl,\n &icsbrec_service_monitor_col_target_availability_zone,\n &icsbrec_service_monitor_col_logical_port);\n \n@@ -3668,12 +3714,13 @@ main(int argc, char *argv[])\n \n /* Initialize incremental processing engine for ovn-northd */\n inc_proc_ic_init(&ovnnb_idl_loop, &ovnsb_idl_loop,\n- &ovninb_idl_loop, &ovnisb_idl_loop);\n+ &ovninb_idl_loop, &ovnisb_unlocked_idl_loop);\n \n unsigned int ovnnb_cond_seqno = UINT_MAX;\n unsigned int ovnsb_cond_seqno = UINT_MAX;\n unsigned int ovninb_cond_seqno = UINT_MAX;\n unsigned int ovnisb_cond_seqno = UINT_MAX;\n+ unsigned int ovnisb_unlocked_cond_seqno = UINT_MAX;\n \n /* Main loop. */\n struct ic_engine_context eng_ctx = {0};\n@@ -3685,7 +3732,9 @@ main(int argc, char *argv[])\n while (!exiting) {\n update_ssl_config();\n update_idl_probe_interval(ovnsb_idl_loop.idl, ovnnb_idl_loop.idl,\n- ovnisb_idl_loop.idl, ovninb_idl_loop.idl);\n+ ovnisb_idl_loop.idl,\n+ ovnisb_unlocked_idl_loop.idl,\n+ ovninb_idl_loop.idl);\n memory_run();\n if (memory_should_report()) {\n struct simap usage = SIMAP_INITIALIZER(&usage);\n@@ -3707,6 +3756,17 @@ main(int argc, char *argv[])\n ovsdb_idl_set_lock(ovnsb_idl_loop.idl, \"ovn_ic\");\n }\n \n+ if (!ovsdb_idl_has_lock(ovnisb_idl_loop.idl) &&\n+ !ovsdb_idl_is_lock_contended(ovnisb_idl_loop.idl)) {\n+ /*\n+ * Ensure that only a single ovn-ic has the permission to\n+ * write to IC-SB.\n+ */\n+ VLOG_INFO(\"OVN ISB lock acquired. \"\n+ \"This ovn-ic instance is now active.\");\n+ ovsdb_idl_set_lock(ovnisb_idl_loop.idl, \"ovn_ic_sb\");\n+ }\n+\n struct ovsdb_idl_txn *ovnnb_txn =\n run_idl_loop(&ovnnb_idl_loop, \"OVN_Northbound\",\n &eng_ctx.nb_idl_duration_ms);\n@@ -3759,6 +3819,20 @@ main(int argc, char *argv[])\n ovnisb_cond_seqno = new_ovnisb_cond_seqno;\n }\n \n+ struct ovsdb_idl_txn *ovnisb_unlocked_txn =\n+ run_idl_loop(&ovnisb_unlocked_idl_loop, \"OVN_IC_Southbound\",\n+ &eng_ctx.isb_unlock_idl_duration_ms);\n+ unsigned int new_ovnisb_unlocked_cond_seqno =\n+ ovsdb_idl_get_condition_seqno(ovnisb_unlocked_idl_loop.idl);\n+ if (new_ovnisb_unlocked_cond_seqno != ovnisb_unlocked_cond_seqno) {\n+ if (!new_ovnisb_unlocked_cond_seqno) {\n+ VLOG_INFO(\"OVN ISB IDL Unlocked reconnected, \"\n+ \"force recompute.\");\n+ inc_proc_ic_force_recompute();\n+ }\n+ ovnisb_unlocked_cond_seqno = new_ovnisb_unlocked_cond_seqno;\n+ }\n+\n struct ic_context ctx = {\n .ovnnb_idl = ovnnb_idl_loop.idl,\n .ovnnb_txn = ovnnb_txn,\n@@ -3768,6 +3842,8 @@ main(int argc, char *argv[])\n .ovninb_txn = ovninb_txn,\n .ovnisb_idl = ovnisb_idl_loop.idl,\n .ovnisb_txn = ovnisb_txn,\n+ .ovnisb_unlocked_idl = ovnisb_unlocked_idl_loop.idl,\n+ .ovnisb_unlocked_txn = ovnisb_unlocked_txn,\n .nbrec_ls_by_name = nbrec_ls_by_name,\n .nbrec_lr_by_name = nbrec_lr_by_name,\n .nbrec_lrp_by_name = nbrec_lrp_by_name,\n@@ -3815,15 +3891,17 @@ main(int argc, char *argv[])\n ovsdb_idl_has_ever_connected(ctx.ovnnb_idl) &&\n ovsdb_idl_has_ever_connected(ctx.ovnsb_idl) &&\n ovsdb_idl_has_ever_connected(ctx.ovninb_idl) &&\n- ovsdb_idl_has_ever_connected(ctx.ovnisb_idl)) {\n+ ovsdb_idl_has_ever_connected(ctx.ovnisb_idl) &&\n+ ovsdb_idl_has_ever_connected(ctx.ovnisb_unlocked_idl)) {\n if (ctx.ovnnb_txn && ctx.ovnsb_txn && ctx.ovninb_txn &&\n- ctx.ovnisb_txn && inc_proc_ic_can_run(&eng_ctx)) {\n+ ctx.ovnisb_unlocked_txn && inc_proc_ic_can_run(&eng_ctx)) {\n ctx.runned_az = az_run(&ctx);\n VLOG_DBG(\"Availability zone: %s\", ctx.runned_az ?\n ctx.runned_az->name : \"not created yet.\");\n if (ctx.runned_az) {\n (void) inc_proc_ic_run(&ctx, &eng_ctx);\n- update_sequence_numbers(&ctx, &ovnisb_idl_loop);\n+ update_sequence_numbers(&ctx,\n+ &ovnisb_unlocked_idl_loop);\n }\n } else if (!inc_proc_ic_get_force_recompute()) {\n clear_idl_track = false;\n@@ -3847,10 +3925,21 @@ main(int argc, char *argv[])\n \"force recompute next time.\");\n inc_proc_ic_force_recompute_immediate();\n }\n+ if (!ovsdb_idl_loop_commit_and_wait(\n+ &ovnisb_unlocked_idl_loop)) {\n+ VLOG_INFO(\"OVNISB Unlocked commit failed, \"\n+ \"force recompute next time.\");\n+ inc_proc_ic_force_recompute_immediate();\n+ }\n \n- if (!ovsdb_idl_loop_commit_and_wait(&ovnisb_idl_loop)) {\n+ /*\n+ * ovn-ic will only try to recompute a failed transaction from\n+ * the locked connection IF the AZ has the lock.\n+ */\n+ if (!ovsdb_idl_loop_commit_and_wait(&ovnisb_idl_loop) &&\n+ ovsdb_idl_has_lock(ovnisb_idl_loop.idl)) {\n VLOG_INFO(\"OVNISB commit failed, \"\n- \"force recompute next time.\");\n+ \"force recompute next time.\");\n inc_proc_ic_force_recompute_immediate();\n }\n } else {\n@@ -3859,10 +3948,13 @@ main(int argc, char *argv[])\n int rc2 = ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);\n int rc3 = ovsdb_idl_loop_commit_and_wait(&ovninb_idl_loop);\n int rc4 = ovsdb_idl_loop_commit_and_wait(&ovnisb_idl_loop);\n- if (!rc1 || !rc2 || !rc3 || !rc4) {\n- VLOG_DBG(\" a transaction failed in: %s %s %s %s\",\n+ int rc5 =\n+ ovsdb_idl_loop_commit_and_wait(&ovnisb_unlocked_idl_loop);\n+ if (!rc1 || !rc2 || !rc3 || !rc4 || !rc5) {\n+ VLOG_DBG(\" a transaction failed in: %s %s %s %s %s\",\n !rc1 ? \"nb\" : \"\", !rc2 ? \"sb\" : \"\",\n- !rc3 ? \"ic_nb\" : \"\", !rc4 ? \"ic_sb\" : \"\");\n+ !rc3 ? \"ic_nb\" : \"\", !rc4 ? \"ic_sb\" : \"\",\n+ !rc5 ? \"ic_sb_unlocked\" : \"\");\n /* A transaction failed. Wake up immediately to give\n * opportunity to send the proper transaction\n */\n@@ -3877,6 +3969,13 @@ main(int argc, char *argv[])\n * copy will be out of sync.\n * - but we don't want to create any txns.\n * */\n+ if (ovsdb_idl_has_lock(ovnisb_idl_loop.idl))\n+ {\n+ VLOG_INFO(\"This ovn-ic instance is now paused. \"\n+ \"Removing IC-SB lock.\");\n+ ovsdb_idl_set_lock(ovnisb_idl_loop.idl, NULL);\n+ }\n+\n if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl) ||\n ovsdb_idl_is_lock_contended(ovnsb_idl_loop.idl))\n {\n@@ -3890,10 +3989,12 @@ main(int argc, char *argv[])\n ovsdb_idl_run(ovnsb_idl_loop.idl);\n ovsdb_idl_run(ovninb_idl_loop.idl);\n ovsdb_idl_run(ovnisb_idl_loop.idl);\n+ ovsdb_idl_run(ovnisb_unlocked_idl_loop.idl);\n ovsdb_idl_wait(ovnnb_idl_loop.idl);\n ovsdb_idl_wait(ovnsb_idl_loop.idl);\n ovsdb_idl_wait(ovninb_idl_loop.idl);\n ovsdb_idl_wait(ovnisb_idl_loop.idl);\n+ ovsdb_idl_wait(ovnisb_unlocked_idl_loop.idl);\n \n /* Force a full recompute next time we become active. */\n inc_proc_ic_force_recompute_immediate();\n@@ -3904,6 +4005,7 @@ main(int argc, char *argv[])\n ovsdb_idl_track_clear(ovnsb_idl_loop.idl);\n ovsdb_idl_track_clear(ovninb_idl_loop.idl);\n ovsdb_idl_track_clear(ovnisb_idl_loop.idl);\n+ ovsdb_idl_track_clear(ovnisb_unlocked_idl_loop.idl);\n }\n \n unixctl_server_run(unixctl);\n@@ -3925,6 +4027,7 @@ main(int argc, char *argv[])\n ovsdb_idl_loop_destroy(&ovnsb_idl_loop);\n ovsdb_idl_loop_destroy(&ovninb_idl_loop);\n ovsdb_idl_loop_destroy(&ovnisb_idl_loop);\n+ ovsdb_idl_loop_destroy(&ovnisb_unlocked_idl_loop);\n service_stop();\n \n exit(res);\ndiff --git a/ic/ovn-ic.h b/ic/ovn-ic.h\nindex 7391a19d4..9f52bb0f9 100644\n--- a/ic/ovn-ic.h\n+++ b/ic/ovn-ic.h\n@@ -22,10 +22,12 @@ struct ic_context {\n struct ovsdb_idl *ovnsb_idl;\n struct ovsdb_idl *ovninb_idl;\n struct ovsdb_idl *ovnisb_idl;\n+ struct ovsdb_idl *ovnisb_unlocked_idl;\n struct ovsdb_idl_txn *ovnnb_txn;\n struct ovsdb_idl_txn *ovnsb_txn;\n struct ovsdb_idl_txn *ovninb_txn;\n struct ovsdb_idl_txn *ovnisb_txn;\n+ struct ovsdb_idl_txn *ovnisb_unlocked_txn;\n const struct icsbrec_availability_zone *runned_az;\n struct ovsdb_idl_index *nbrec_ls_by_name;\n struct ovsdb_idl_index *nbrec_lr_by_name;\ndiff --git a/tests/ovn-ic.at b/tests/ovn-ic.at\nindex 50270fdda..68adc480c 100644\n--- a/tests/ovn-ic.at\n+++ b/tests/ovn-ic.at\n@@ -4793,3 +4793,47 @@ OVN_CLEANUP_IC([az1], [az2])\n \n AT_CLEANUP\n ])\n+\n+OVN_FOR_EACH_NORTHD([\n+AT_SETUP([ovn-ic - IC-SB lock acquisition and transit switch creation])\n+\n+ovn_init_ic_db\n+ovn_start az1\n+ovn_start az2\n+\n+wait_row_count ic-sb:Availability_Zone 2\n+\n+ovn_as az1\n+check ovn-ic-nbctl ts-add ts1\n+\n+wait_row_count ic-sb:Datapath_Binding 1 transit_switch=ts1\n+as az1 check ovn-appctl -t ic/ovn-ic pause\n+\n+ovn_as az2\n+check ovn-ic-nbctl ts-add ts2\n+wait_row_count ic-sb:Datapath_Binding 1 transit_switch=ts2\n+\n+check_row_count ic-sb:Datapath_Binding 2\n+check_column \"ts1 ts2\" ic-sb:Datapath_Binding transit_switch\n+\n+as az1 check ovn-appctl -t ic/ovn-ic resume\n+\n+as az2 check ovn-appctl -t ic/ovn-ic pause\n+\n+ovn_as az1\n+check ovn-ic-nbctl ts-add ts3\n+\n+wait_row_count ic-sb:Datapath_Binding 1 transit_switch=ts3\n+check_row_count ic-sb:Datapath_Binding 3\n+check_column \"ts1 ts2 ts3\" ic-sb:Datapath_Binding transit_switch\n+\n+as az2 check ovn-appctl -t ic/ovn-ic resume\n+\n+for i in 1 2; do\n+ az=az$i\n+ ovn_as $az\n+ OVN_CLEANUP_AZ([$az])\n+done\n+\n+AT_CLEANUP\n+])\n\\ No newline at end of file\n", "prefixes": [ "ovs-dev", "v4" ] }