Skip to content

Commit

Permalink
ovsdb: Implement garbage collection.
Browse files Browse the repository at this point in the history
  • Loading branch information
blp committed Mar 10, 2011
1 parent 7f90cb0 commit c5f341a
Show file tree
Hide file tree
Showing 27 changed files with 800 additions and 313 deletions.
12 changes: 12 additions & 0 deletions debian/openvswitch-switch.init
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,18 @@ case "$1" in
cksum=`ovsdb-tool db-cksum "$conf_file" | awk '{print $1}'`
cp "$conf_file" "$conf_file.backup$version-$cksum"

# Compact database. This is important if the old schema did not
# enable garbage collection (i.e. if it did not have any tables
# with "isRoot": true) but the new schema does. In that situation
# the old database may contain a transaction that creates a record
# followed by a transaction that creates the first use of the
# record. Replaying that series of transactions against the new
# database schema (as "convert" does) would cause the record to be
# dropped by the first transaction, then the second transaction
# would cause a referential integrity failure (for a strong
# reference).
ovsdb-tool -vANY:console:emer compact $conf_file

# Upgrade or downgrade schema and compact database.
ovsdb-tool -vANY:console:emer convert $conf_file $schema_file
fi
Expand Down
60 changes: 48 additions & 12 deletions lib/ovsdb-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,28 @@ parse_json_pair(const struct json *json,
return NULL;
}

static void
ovsdb_symbol_referenced(struct ovsdb_symbol *symbol,
const struct ovsdb_base_type *base)
{
assert(base->type == OVSDB_TYPE_UUID);

if (base->u.uuid.refTableName) {
switch (base->u.uuid.refType) {
case OVSDB_REF_STRONG:
symbol->strong_ref = true;
break;
case OVSDB_REF_WEAK:
symbol->weak_ref = true;
break;
}
}
}

static struct ovsdb_error * WARN_UNUSED_RESULT
ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
struct ovsdb_symbol_table *symtab)
struct ovsdb_symbol_table *symtab,
const struct ovsdb_base_type *base)
{
struct ovsdb_error *error0;
const struct json *value;
Expand All @@ -304,14 +323,17 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,

error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value);
if (!error1) {
const char *name = json_string(value);
struct ovsdb_symbol *symbol;

ovsdb_error_destroy(error0);
*uuid = ovsdb_symbol_table_insert(symtab, name)->uuid;
if (!ovsdb_parser_is_id(json_string(value))) {
return ovsdb_syntax_error(json, NULL, "named-uuid string is "
"not a valid <id>");
}

symbol = ovsdb_symbol_table_insert(symtab, json_string(value));
*uuid = symbol->uuid;
ovsdb_symbol_referenced(symbol, base);
return NULL;
}
ovsdb_error_destroy(error1);
Expand All @@ -321,10 +343,13 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
}

static struct ovsdb_error * WARN_UNUSED_RESULT
ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
ovsdb_atom_from_json__(union ovsdb_atom *atom,
const struct ovsdb_base_type *base,
const struct json *json,
struct ovsdb_symbol_table *symtab)
{
enum ovsdb_atomic_type type = base->type;

switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
Expand Down Expand Up @@ -364,7 +389,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
break;

case OVSDB_TYPE_UUID:
return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab);
return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab, base);

case OVSDB_N_TYPES:
default:
Expand All @@ -384,7 +409,9 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
*
* If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to
* ovsdb/SPECS for information about this, and for the syntax that this
* function accepts. */
* function accepts. If 'base' is a reference and a symbol is parsed, then the
* symbol's 'strong_ref' or 'weak_ref' member is set to true, as
* appropriate. */
struct ovsdb_error *
ovsdb_atom_from_json(union ovsdb_atom *atom,
const struct ovsdb_base_type *base,
Expand All @@ -393,7 +420,7 @@ ovsdb_atom_from_json(union ovsdb_atom *atom,
{
struct ovsdb_error *error;

error = ovsdb_atom_from_json__(atom, base->type, json, symtab);
error = ovsdb_atom_from_json__(atom, base, json, symtab);
if (error) {
return error;
}
Expand Down Expand Up @@ -440,9 +467,12 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
}

static char *
ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
const char *s, struct ovsdb_symbol_table *symtab)
ovsdb_atom_from_string__(union ovsdb_atom *atom,
const struct ovsdb_base_type *base, const char *s,
struct ovsdb_symbol_table *symtab)
{
enum ovsdb_atomic_type type = base->type;

switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
Expand Down Expand Up @@ -503,7 +533,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,

case OVSDB_TYPE_UUID:
if (*s == '@') {
atom->uuid = ovsdb_symbol_table_insert(symtab, s)->uuid;
struct ovsdb_symbol *symbol = ovsdb_symbol_table_insert(symtab, s);
atom->uuid = symbol->uuid;
ovsdb_symbol_referenced(symbol, base);
} else if (!uuid_from_string(&atom->uuid, s)) {
return xasprintf("\"%s\" is not a valid UUID", s);
}
Expand Down Expand Up @@ -535,7 +567,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
* then an identifier beginning with '@' is also acceptable. If the
* named identifier is already in 'symtab', then the associated UUID is
* used; otherwise, a new, random UUID is used and added to the symbol
* table.
* table. If 'base' is a reference and a symbol is parsed, then the
* symbol's 'strong_ref' or 'weak_ref' member is set to true, as
* appropriate.
*
* Returns a null pointer if successful, otherwise an error message describing
* the problem. On failure, the contents of 'atom' are indeterminate. The
Expand All @@ -549,7 +583,7 @@ ovsdb_atom_from_string(union ovsdb_atom *atom,
struct ovsdb_error *error;
char *msg;

msg = ovsdb_atom_from_string__(atom, base->type, s, symtab);
msg = ovsdb_atom_from_string__(atom, base, s, symtab);
if (msg) {
return msg;
}
Expand Down Expand Up @@ -1829,6 +1863,8 @@ ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name,
symbol = xmalloc(sizeof *symbol);
symbol->uuid = *uuid;
symbol->created = created;
symbol->strong_ref = false;
symbol->weak_ref = false;
shash_add(&symtab->sh, name, symbol);
return symbol;
}
Expand Down
2 changes: 2 additions & 0 deletions lib/ovsdb-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ struct ovsdb_symbol_table {
struct ovsdb_symbol {
struct uuid uuid; /* The UUID that the symbol represents. */
bool created; /* Already used to create row? */
bool strong_ref; /* Parsed a strong reference to this row? */
bool weak_ref; /* Parsed a weak reference to this row? */
};

struct ovsdb_symbol_table *ovsdb_symbol_table_create(void);
Expand Down
3 changes: 2 additions & 1 deletion lib/ovsdb-idl-provider.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (c) 2009, 2010 Nicira Networks.
/* Copyright (c) 2009, 2010, 2011 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -47,6 +47,7 @@ struct ovsdb_idl_column {

struct ovsdb_idl_table_class {
char *name;
bool is_root;
const struct ovsdb_idl_column *columns;
size_t n_columns;
size_t allocation_size;
Expand Down
19 changes: 18 additions & 1 deletion ovsdb/SPECS
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ is represented by <database-schema>, as described below.

"columns": {<id>: <column-schema>, ...} required
"maxRows": <integer> optional
"isRoot": <boolean> optional

The value of "columns" is a JSON object whose names are column
names and whose values are <column-schema>s.
Expand All @@ -152,12 +153,28 @@ is represented by <database-schema>, as described below.
the database process is stopped and then started again, each
"_version" also changes to a new random value.

If "isRoot" is omitted or specified as false, then any given row
in the table may exist only when there is at least one reference
to it, with refType "strong", from a different row (in the same
table or a different table). This is a "deferred" action:
unreferenced rows in the table are deleted just before transaction
commit. If "isRoot" is specified as true, then rows in the table
exist independent of any references (they can be thought of as
part of the "root set" in a garbage collector).

For compatibility with schemas created before "isRoot" was
introduced, if "isRoot" is omitted or false in every
<table-schema> in a given <database-schema>, then every table is
part of the root set.

If "maxRows" is specified, as a positive integer, it limits the
maximum number of rows that may be present in the table. This is
a "deferred" constraint, enforced only at transaction commit time
(see the "transact" request below). If "maxRows" is not
specified, the size of the table is limited only by the resources
available to the database server.
available to the database server. "maxRows" constraints are
enforced after unreferenced rows are deleted from tables with a
false "isRoot".

<column-schema>

Expand Down
11 changes: 10 additions & 1 deletion ovsdb/dot2pic
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#! /usr/bin/perl

# Copyright (c) 2009, 2010 Nicira Networks
# Copyright (c) 2009, 2010, 2011 Nicira Networks
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,7 +29,14 @@ while (<>) {
$y *= $scale;
$width *= $scale;
$height *= $scale;
print "linethick = ", ($style eq 'bold' ? 0.5 : 1.0), ";\n";
print "box at $x,$y wid $width height $height \"$name\"\n";
if ($style eq 'bold') {
my $inset = 2.0 / 72.0;
$width -= $inset * 2;
$height -= $inset * 2;
print "box at $x,$y wid $width height $height\n";
}
} elsif (/edge/) {
my (undef, $tail, $head, $n, $rest) = split(' ', $_, 5);
my @xy;
Expand All @@ -51,6 +58,8 @@ while (<>) {
}
my ($style, $color) = split(' ', $rest);

print "linethick = ", ($style eq 'dotted' ? 0.5 : 1), ";\n";

print "spline -> from $xy[0][0],$xy[0][1]";
for (my ($i) = 0; $i <= $#xy; $i++) {
print " to $xy[$i][0],$xy[$i][1]";
Expand Down
6 changes: 4 additions & 2 deletions ovsdb/ovsdb-doc.in
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,11 @@ Table Purpose
.SH "TABLE RELATIONSHIPS"
.PP
The following diagram shows the relationship among tables in the
database. Each node represents a table. Each edge leads from the
database. Each node represents a table. Tables that are part of the
``root set'' are shown with double borders. Each edge leads from the
table that contains it and points to the table that its value
represents. Edges are labeled with their column names.
represents. Edges are labeled with their column names. Thick lines
represent strong references; thin lines represent weak references.
.RS -1in
"""
erStream = open(erFile, "r")
Expand Down
16 changes: 11 additions & 5 deletions ovsdb/ovsdb-dot.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def printEdge(tableName, baseType, label):
options['label'] = '"%s"' % label
if baseType.ref_type == 'weak':
options['constraint'] = 'false'
options['style'] = 'dotted'
print "\t%s -> %s [%s];" % (
tableName,
baseType.ref_table,
Expand All @@ -25,12 +26,17 @@ def schemaToDot(schemaFile):
schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile))

print "digraph %s {" % schema.name
print '\tsize="6.5,4";'
print '\tmargin="0";'
print "\tnode [shape=box];"
print "\tedge [dir=none, arrowhead=none, arrowtail=none];"
for tableName, table in schema.tables.iteritems():
print '\tsize="6.5,4";'
print '\tmargin="0";'
print "\tnode [shape=box];"
print "\tedge [dir=none, arrowhead=none, arrowtail=none];"
print "\t%s;" % tableName
options = {}
if table.is_root:
options['style'] = 'bold'
print "\t%s [%s];" % (
tableName,
', '.join(['%s=%s' % (k,v) for k,v in options.items()]))
for columnName, column in table.columns.iteritems():
if column.type.value:
printEdge(tableName, column.type.key, "%s key" % columnName)
Expand Down
6 changes: 5 additions & 1 deletion ovsdb/ovsdb-idlc.in
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,11 @@ static void\n%s_columns_init(void)
print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
for tableName, table in sorted(schema.tables.iteritems()):
structName = "%s%s" % (prefix, tableName.lower())
print " {\"%s\"," % tableName
if table.is_root:
is_root = "true"
else:
is_root = "false"
print " {\"%s\", %s," % (tableName, is_root)
print " %s_columns, ARRAY_SIZE(%s_columns)," % (
structName, structName)
print " sizeof(struct %s)}," % structName
Expand Down
36 changes: 35 additions & 1 deletion ovsdb/ovsdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ is_valid_version(const char *s)
return n != -1 && s[n] == '\0';
}

/* Returns the number of tables in 'schema''s root set. */
static size_t
root_set_size(const struct ovsdb_schema *schema)
{
struct shash_node *node;
size_t n_root;

SHASH_FOR_EACH (node, &schema->tables) {
struct ovsdb_table_schema *table = node->data;

n_root += table->is_root;
}
return n_root;
}

struct ovsdb_error *
ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
{
Expand Down Expand Up @@ -205,6 +220,18 @@ ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
}
}

/* "isRoot" was not part of the original schema definition. Before it was
* added, there was no support for garbage collection. So, for backward
* compatibility, if the root set is empty then assume that every table is
* in the root set. */
if (root_set_size(schema) == 0) {
SHASH_FOR_EACH (node, &schema->tables) {
struct ovsdb_table_schema *table = node->data;

table->is_root = true;
}
}

*schemap = schema;
return 0;
}
Expand All @@ -214,6 +241,7 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema)
{
struct json *json, *tables;
struct shash_node *node;
bool default_is_root;

json = json_object_create();
json_object_put_string(json, "name", schema->name);
Expand All @@ -224,12 +252,18 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema)
json_object_put_string(json, "cksum", schema->cksum);
}

/* "isRoot" was not part of the original schema definition. Before it was
* added, there was no support for garbage collection. So, for backward
* compatibility, if every table is in the root set then do not output
* "isRoot" in table schemas. */
default_is_root = root_set_size(schema) == shash_count(&schema->tables);

tables = json_object_create();

SHASH_FOR_EACH (node, &schema->tables) {
struct ovsdb_table_schema *table = node->data;
json_object_put(tables, table->name,
ovsdb_table_schema_to_json(table));
ovsdb_table_schema_to_json(table, default_is_root));
}
json_object_put(json, "tables", tables);

Expand Down
Loading

0 comments on commit c5f341a

Please sign in to comment.