2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00
ovs/ovsdb/storage.h
Ilya Maximets f8ed13355d ovsdb: raft: Don't forward more than one command to the leader.
Every transaction has RAFT log prerequisites.  Even if transactions
are not related (because RAFT doesn't actually know what data it is
handling).  When leader writes a new record to a RAFT storage, it is
getting appended to the log right away and changes current 'eid',
i.e., changes prerequisites.  The leader will not try to write new
records until the current one is committed, because until then the
pre-check will be failing.

However, that is different for the follower.  Followers do not add
records to the RAFT log until the leader sends an append request back.
So, if there are multiple transactions pending on a follower, it will
create a command for each of them and prerequisites will be set to the
same values.  All these commands will be sent to the leader, but only
one can succeed at a time, because accepting one command immediately
changes prerequisites and all other commands become non-applicable.
So, out of N commands, 1 will succeed and N - 1 will fail.  The cluster
failure is a transient failure, so the follower will re-process all the
failed transactions and send them again.  1 will succeed and N - 2 will
fail.  And so on, until there are no more transactions.  In the end,
instead of processing N transactions, the follower is performing
N * (N - 1) / 2 transaction processing iterations.  That is consuming
a huge amount of CPU resources completely unnecessarily.

Since there is no real chance for multiple transactions from the same
follower to succeed, it's better to not send them in the first place.
This also eliminates prerequisite mismatch messages on a leader in
this particular case.

In a test with 30 parallel shell threads executing 12K transactions
total with separate ovsdb-client calls through the same follower there
is about 60% performance improvement.  The test takes ~100 seconds to
complete without this change and ~40 seconds with this change applied.
The new time is very close to what it takes to execute the same test
through the cluster leader.  The test can be found at the link below.

Note: prerequisite failures on a leader are still possible, but mostly
in a case of simultaneous transactions from different followers.  It's
a normal thing for a distributed database due to its nature.

Link: https://mail.openvswitch.org/pipermail/ovs-dev/2024-June/415167.html
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2024-07-08 22:13:20 +02:00

105 lines
4.5 KiB
C

/* Copyright (c) 2009, 2010, 2011, 2016, 2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this storage except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OVSDB_STORAGE_H
#define OVSDB_STORAGE_H 1
#include <stdint.h>
#include <sys/types.h>
#include "compiler.h"
struct json;
struct ovsdb_schema;
struct ovsdb_storage;
struct simap;
struct uuid;
struct ovsdb_error *ovsdb_storage_open(const char *filename, bool rw,
struct ovsdb_storage **)
OVS_WARN_UNUSED_RESULT;
struct ovsdb_storage *ovsdb_storage_create_unbacked(const char *name);
void ovsdb_storage_close(struct ovsdb_storage *);
const char *ovsdb_storage_get_model(const struct ovsdb_storage *);
bool ovsdb_storage_is_clustered(const struct ovsdb_storage *);
bool ovsdb_storage_is_connected(const struct ovsdb_storage *);
bool ovsdb_storage_is_dead(const struct ovsdb_storage *);
bool ovsdb_storage_is_leader(const struct ovsdb_storage *);
const struct uuid *ovsdb_storage_get_cid(const struct ovsdb_storage *);
const struct uuid *ovsdb_storage_get_sid(const struct ovsdb_storage *);
uint64_t ovsdb_storage_get_applied_index(const struct ovsdb_storage *);
void ovsdb_storage_get_memory_usage(const struct ovsdb_storage *,
struct simap *usage);
char *ovsdb_storage_get_error(const struct ovsdb_storage *);
void ovsdb_storage_run(struct ovsdb_storage *);
void ovsdb_storage_wait(struct ovsdb_storage *);
const char *ovsdb_storage_get_name(const struct ovsdb_storage *);
struct ovsdb_error *ovsdb_storage_read(struct ovsdb_storage *,
struct ovsdb_schema **schemap,
struct json **txnp,
struct uuid *txnid)
OVS_WARN_UNUSED_RESULT;
bool ovsdb_storage_read_wait(struct ovsdb_storage *);
void ovsdb_storage_unread(struct ovsdb_storage *);
struct ovsdb_write *ovsdb_storage_write(struct ovsdb_storage *,
const struct json *,
const struct uuid *prereq,
struct uuid *result,
bool durable)
OVS_WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_storage_write_block(struct ovsdb_storage *,
const struct json *,
const struct uuid *prereq,
struct uuid *result,
bool durable);
bool ovsdb_write_is_complete(const struct ovsdb_write *);
const struct ovsdb_error *ovsdb_write_get_error(const struct ovsdb_write *);
uint64_t ovsdb_write_get_commit_index(const struct ovsdb_write *);
void ovsdb_write_wait(const struct ovsdb_write *);
void ovsdb_write_destroy(struct ovsdb_write *);
bool ovsdb_storage_should_snapshot(struct ovsdb_storage *);
struct ovsdb_error *ovsdb_storage_store_snapshot(struct ovsdb_storage *storage,
const struct json *schema,
const struct json *snapshot,
uint64_t applied_index)
OVS_WARN_UNUSED_RESULT;
struct ovsdb_write *ovsdb_storage_write_schema_change(
struct ovsdb_storage *,
const struct ovsdb_schema *, const struct json *data,
const struct uuid *prereq, struct uuid *result)
OVS_WARN_UNUSED_RESULT;
/* Convenience functions for ovsdb-tool and other command-line utilities,
* for use with standalone database files only, which terminate the process
* on error. */
struct ovsdb_storage *ovsdb_storage_open_standalone(const char *filename,
bool rw);
struct ovsdb_schema *ovsdb_storage_read_schema(struct ovsdb_storage *);
/* Checks that there is a chance for a record with specified prerequisites
* to be successfully written to the storage. */
bool ovsdb_storage_precheck_prereq(const struct ovsdb_storage *,
const struct uuid *prereq);
#endif /* ovsdb/storage.h */