diff mbox series

[ovs-dev,2/4] ovsdb: transaction: Keep one entry in the transaction history.

Message ID 20211219140941.2279071-3-i.maximets@ovn.org
State Accepted
Commit 6e13565dd32fb2cf5517f51ca06956e2052c4bba
Headers show
Series ovsdb: Bug fixes + Relay txn history and performance. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

Ilya Maximets Dec. 19, 2021, 2:09 p.m. UTC
If a single transaction exceeds the size of the whole database (e.g.,
a lot of rows got removed and new ones added), transaction history will
be drained.  This leads to sending UUID_ZERO to the clients as the last
transaction id in the next monitor update, because monitor doesn't
know what was the actual last transaction id.  In case of a re-connect
that will cause re-downloading of the whole database, since the
client's last_id will be out of sync.

One solution would be to store the last transaction ID separately
from the actual transactions, but that will require a careful
management in cases where database gets reset and the history needs
to be cleared.  Keeping the one last transaction instead to avoid
the problem.  That should not be a big concern in terms of memory
consumption, because this last transaction will be removed from the
history once the next transaction appeared.  This is also not a concern
for a fast re-sync, because this last transaction will not be used
for the monitor reply; it's either client already has it, so no need
to send, or it's a history miss.

The test updated to not check the number of atoms if there is only
one transaction in the history.

Fixes: 317b1bfd7dd3 ("ovsdb: Don't let transaction history grow larger than the database.")
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
 ovsdb/transaction.c   |  6 ++++--
 tests/ovsdb-server.at | 16 +++++++++-------
 2 files changed, 13 insertions(+), 9 deletions(-)

Comments

Mike Pattrick Jan. 7, 2022, 8:18 p.m. UTC | #1
On Sun, 2021-12-19 at 15:09 +0100, Ilya Maximets wrote:
> If a single transaction exceeds the size of the whole database (e.g.,
> a lot of rows got removed and new ones added), transaction history
> will
> be drained.  This leads to sending UUID_ZERO to the clients as the
> last
> transaction id in the next monitor update, because monitor doesn't
> know what was the actual last transaction id.  In case of a re-
> connect
> that will cause re-downloading of the whole database, since the
> client's last_id will be out of sync.
> 
> One solution would be to store the last transaction ID separately
> from the actual transactions, but that will require a careful
> management in cases where database gets reset and the history needs
> to be cleared.  Keeping the one last transaction instead to avoid
> the problem.  That should not be a big concern in terms of memory
> consumption, because this last transaction will be removed from the
> history once the next transaction appeared.  This is also not a
> concern
> for a fast re-sync, because this last transaction will not be used
> for the monitor reply; it's either client already has it, so no need
> to send, or it's a history miss.
> 
> The test updated to not check the number of atoms if there is only
> one transaction in the history.
> 
> Fixes: 317b1bfd7dd3 ("ovsdb: Don't let transaction history grow
> larger than the database.")
> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
> ---
>  ovsdb/transaction.c   |  6 ++++--
>  tests/ovsdb-server.at | 16 +++++++++-------
>  2 files changed, 13 insertions(+), 9 deletions(-)
> 


Acked-by: Mike Pattrick <mkp@redhat.com>

> diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
> index 88e052800..db86d847c 100644
> --- a/ovsdb/transaction.c
> +++ b/ovsdb/transaction.c
> @@ -1595,8 +1595,10 @@ ovsdb_txn_history_run(struct ovsdb *db)
>      /* Remove old histories to limit the size of the
> history.  Removing until
>       * the number of ovsdb atoms in history becomes less than the
> number of
>       * atoms in the database, because it will be faster to just get
> a database
> -     * snapshot than re-constructing changes from the history that
> big. */
> -    while (db->n_txn_history &&
> +     * snapshot than re-constructing changes from the history that
> big.
> +     * Keeping at least one transaction to avoid sending UUID_ZERO
> as a last id
> +     * if all entries got removed due to the size limit. */
> +    while (db->n_txn_history > 1 &&
>             (db->n_txn_history > 100 ||
>              db->n_txn_history_atoms > db->n_atoms)) {
>          struct ovsdb_txn_history_node *txn_h_node = CONTAINER_OF(
> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
> index 2b742f78b..876cb836c 100644
> --- a/tests/ovsdb-server.at
> +++ b/tests/ovsdb-server.at
> @@ -1250,10 +1250,11 @@ dnl the total database size, so the
> transaction history will not be able
>  dnl to hold all of them.
>  dnl
>  dnl The test verifies that the number of atoms in the transaction
> history
> -dnl is always less than the number of atoms in the database.
> -get_n_atoms () {
> +dnl is always less than the number of atoms in the database, except
> for
> +dnl a case where there is only one transaction in a history.
> +get_memory_value () {
>      n=$(ovs-appctl -t ovsdb-server memory/show dnl
> -            | tr ' ' '\n' | grep atoms | grep "^$1:" | cut -d ':' -f
> 2)
> +            | tr ' ' '\n' | grep "^$1:" | cut -d ':' -f 2)
>      if test X"$n" == "X"; then
>          n=0
>      fi
> @@ -1261,8 +1262,9 @@ get_n_atoms () {
>  }
>  
>  check_atoms () {
> -    n_db_atoms=$(get_n_atoms atoms)
> -    n_txn_history_atoms=$(get_n_atoms txn-history-atoms)
> +    if test $(get_memory_value txn-history) -eq 1; then return; fi
> +    n_db_atoms=$(get_memory_value atoms)
> +    n_txn_history_atoms=$(get_memory_value txn-history-atoms)
>      echo "n_db_atoms:          $n_db_atoms"
>      echo "n_txn_history_atoms: $n_txn_history_atoms"
>      AT_CHECK([test $n_txn_history_atoms -le $n_db_atoms])
> @@ -1274,7 +1276,7 @@ add_ports () {
>      done
>  }
>  
> -initial_db_atoms=$(get_n_atoms atoms)
> +initial_db_atoms=$(get_memory_value atoms)
>  
>  for i in $(seq 1 100); do
>      cmd=$(add_ports $i $(($i / 4 + 1)))
> @@ -1289,7 +1291,7 @@ done
>  
>  dnl After removing all the bridges, the number of atoms in the
> database
>  dnl should return to its initial value.
> -AT_CHECK([test $(get_n_atoms atoms) -eq $initial_db_atoms])
> +AT_CHECK([test $(get_memory_value atoms) -eq $initial_db_atoms])
>  
>  OVS_APP_EXIT_AND_WAIT([ovsdb-server])
>  AT_CLEANUP
Han Zhou Jan. 25, 2022, 1:34 a.m. UTC | #2
On Sun, Dec 19, 2021 at 6:09 AM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> If a single transaction exceeds the size of the whole database (e.g.,
> a lot of rows got removed and new ones added), transaction history will
> be drained.  This leads to sending UUID_ZERO to the clients as the last
> transaction id in the next monitor update, because monitor doesn't
> know what was the actual last transaction id.  In case of a re-connect
> that will cause re-downloading of the whole database, since the
> client's last_id will be out of sync.
>
> One solution would be to store the last transaction ID separately
> from the actual transactions, but that will require a careful
> management in cases where database gets reset and the history needs
> to be cleared.  Keeping the one last transaction instead to avoid
> the problem.  That should not be a big concern in terms of memory
> consumption, because this last transaction will be removed from the
> history once the next transaction appeared.  This is also not a concern
> for a fast re-sync, because this last transaction will not be used
> for the monitor reply; it's either client already has it, so no need
> to send, or it's a history miss.
>
> The test updated to not check the number of atoms if there is only
> one transaction in the history.
>
> Fixes: 317b1bfd7dd3 ("ovsdb: Don't let transaction history grow larger
than the database.")
> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
> ---
>  ovsdb/transaction.c   |  6 ++++--
>  tests/ovsdb-server.at | 16 +++++++++-------
>  2 files changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
> index 88e052800..db86d847c 100644
> --- a/ovsdb/transaction.c
> +++ b/ovsdb/transaction.c
> @@ -1595,8 +1595,10 @@ ovsdb_txn_history_run(struct ovsdb *db)
>      /* Remove old histories to limit the size of the history.  Removing
until
>       * the number of ovsdb atoms in history becomes less than the number
of
>       * atoms in the database, because it will be faster to just get a
database
> -     * snapshot than re-constructing changes from the history that big.
*/
> -    while (db->n_txn_history &&
> +     * snapshot than re-constructing changes from the history that big.
> +     * Keeping at least one transaction to avoid sending UUID_ZERO as a
last id
> +     * if all entries got removed due to the size limit. */
> +    while (db->n_txn_history > 1 &&
>             (db->n_txn_history > 100 ||
>              db->n_txn_history_atoms > db->n_atoms)) {
>          struct ovsdb_txn_history_node *txn_h_node = CONTAINER_OF(
> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
> index 2b742f78b..876cb836c 100644
> --- a/tests/ovsdb-server.at
> +++ b/tests/ovsdb-server.at
> @@ -1250,10 +1250,11 @@ dnl the total database size, so the transaction
history will not be able
>  dnl to hold all of them.
>  dnl
>  dnl The test verifies that the number of atoms in the transaction history
> -dnl is always less than the number of atoms in the database.
> -get_n_atoms () {
> +dnl is always less than the number of atoms in the database, except for
> +dnl a case where there is only one transaction in a history.
> +get_memory_value () {
>      n=$(ovs-appctl -t ovsdb-server memory/show dnl
> -            | tr ' ' '\n' | grep atoms | grep "^$1:" | cut -d ':' -f 2)
> +            | tr ' ' '\n' | grep "^$1:" | cut -d ':' -f 2)
>      if test X"$n" == "X"; then
>          n=0
>      fi
> @@ -1261,8 +1262,9 @@ get_n_atoms () {
>  }
>
>  check_atoms () {
> -    n_db_atoms=$(get_n_atoms atoms)
> -    n_txn_history_atoms=$(get_n_atoms txn-history-atoms)
> +    if test $(get_memory_value txn-history) -eq 1; then return; fi
> +    n_db_atoms=$(get_memory_value atoms)
> +    n_txn_history_atoms=$(get_memory_value txn-history-atoms)
>      echo "n_db_atoms:          $n_db_atoms"
>      echo "n_txn_history_atoms: $n_txn_history_atoms"
>      AT_CHECK([test $n_txn_history_atoms -le $n_db_atoms])
> @@ -1274,7 +1276,7 @@ add_ports () {
>      done
>  }
>
> -initial_db_atoms=$(get_n_atoms atoms)
> +initial_db_atoms=$(get_memory_value atoms)
>
>  for i in $(seq 1 100); do
>      cmd=$(add_ports $i $(($i / 4 + 1)))
> @@ -1289,7 +1291,7 @@ done
>
>  dnl After removing all the bridges, the number of atoms in the database
>  dnl should return to its initial value.
> -AT_CHECK([test $(get_n_atoms atoms) -eq $initial_db_atoms])
> +AT_CHECK([test $(get_memory_value atoms) -eq $initial_db_atoms])
>
>  OVS_APP_EXIT_AND_WAIT([ovsdb-server])
>  AT_CLEANUP
> --
> 2.31.1
>

Acked-by: Han Zhou <hzhou@ovn.org>
Ilya Maximets Jan. 31, 2022, 10:37 p.m. UTC | #3
On 1/25/22 02:34, Han Zhou wrote:
> 
> 
> On Sun, Dec 19, 2021 at 6:09 AM Ilya Maximets <i.maximets@ovn.org <mailto:i.maximets@ovn.org>> wrote:
>>
>> If a single transaction exceeds the size of the whole database (e.g.,
>> a lot of rows got removed and new ones added), transaction history will
>> be drained.  This leads to sending UUID_ZERO to the clients as the last
>> transaction id in the next monitor update, because monitor doesn't
>> know what was the actual last transaction id.  In case of a re-connect
>> that will cause re-downloading of the whole database, since the
>> client's last_id will be out of sync.
>>
>> One solution would be to store the last transaction ID separately
>> from the actual transactions, but that will require a careful
>> management in cases where database gets reset and the history needs
>> to be cleared.  Keeping the one last transaction instead to avoid
>> the problem.  That should not be a big concern in terms of memory
>> consumption, because this last transaction will be removed from the
>> history once the next transaction appeared.  This is also not a concern
>> for a fast re-sync, because this last transaction will not be used
>> for the monitor reply; it's either client already has it, so no need
>> to send, or it's a history miss.
>>
>> The test updated to not check the number of atoms if there is only
>> one transaction in the history.
>>
>> Fixes: 317b1bfd7dd3 ("ovsdb: Don't let transaction history grow larger than the database.")
>> Signed-off-by: Ilya Maximets <i.maximets@ovn.org <mailto:i.maximets@ovn.org>>
>> ---
>>  ovsdb/transaction.c   |  6 ++++--
>>  tests/ovsdb-server.at <http://ovsdb-server.at> | 16 +++++++++-------
>>  2 files changed, 13 insertions(+), 9 deletions(-)
>>
>>
> 
> Acked-by: Han Zhou <hzhou@ovn.org <mailto:hzhou@ovn.org>>

Thanks, Han and Mike!  Applied and backported to branch-2.17.

Bets regards, Ilya Maximets.
diff mbox series

Patch

diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
index 88e052800..db86d847c 100644
--- a/ovsdb/transaction.c
+++ b/ovsdb/transaction.c
@@ -1595,8 +1595,10 @@  ovsdb_txn_history_run(struct ovsdb *db)
     /* Remove old histories to limit the size of the history.  Removing until
      * the number of ovsdb atoms in history becomes less than the number of
      * atoms in the database, because it will be faster to just get a database
-     * snapshot than re-constructing changes from the history that big. */
-    while (db->n_txn_history &&
+     * snapshot than re-constructing changes from the history that big.
+     * Keeping at least one transaction to avoid sending UUID_ZERO as a last id
+     * if all entries got removed due to the size limit. */
+    while (db->n_txn_history > 1 &&
            (db->n_txn_history > 100 ||
             db->n_txn_history_atoms > db->n_atoms)) {
         struct ovsdb_txn_history_node *txn_h_node = CONTAINER_OF(
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index 2b742f78b..876cb836c 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -1250,10 +1250,11 @@  dnl the total database size, so the transaction history will not be able
 dnl to hold all of them.
 dnl
 dnl The test verifies that the number of atoms in the transaction history
-dnl is always less than the number of atoms in the database.
-get_n_atoms () {
+dnl is always less than the number of atoms in the database, except for
+dnl a case where there is only one transaction in a history.
+get_memory_value () {
     n=$(ovs-appctl -t ovsdb-server memory/show dnl
-            | tr ' ' '\n' | grep atoms | grep "^$1:" | cut -d ':' -f 2)
+            | tr ' ' '\n' | grep "^$1:" | cut -d ':' -f 2)
     if test X"$n" == "X"; then
         n=0
     fi
@@ -1261,8 +1262,9 @@  get_n_atoms () {
 }
 
 check_atoms () {
-    n_db_atoms=$(get_n_atoms atoms)
-    n_txn_history_atoms=$(get_n_atoms txn-history-atoms)
+    if test $(get_memory_value txn-history) -eq 1; then return; fi
+    n_db_atoms=$(get_memory_value atoms)
+    n_txn_history_atoms=$(get_memory_value txn-history-atoms)
     echo "n_db_atoms:          $n_db_atoms"
     echo "n_txn_history_atoms: $n_txn_history_atoms"
     AT_CHECK([test $n_txn_history_atoms -le $n_db_atoms])
@@ -1274,7 +1276,7 @@  add_ports () {
     done
 }
 
-initial_db_atoms=$(get_n_atoms atoms)
+initial_db_atoms=$(get_memory_value atoms)
 
 for i in $(seq 1 100); do
     cmd=$(add_ports $i $(($i / 4 + 1)))
@@ -1289,7 +1291,7 @@  done
 
 dnl After removing all the bridges, the number of atoms in the database
 dnl should return to its initial value.
-AT_CHECK([test $(get_n_atoms atoms) -eq $initial_db_atoms])
+AT_CHECK([test $(get_memory_value atoms) -eq $initial_db_atoms])
 
 OVS_APP_EXIT_AND_WAIT([ovsdb-server])
 AT_CLEANUP