Merge remote-tracking branch 'origin/topic/johanna/table-changes'

* origin/topic/johanna/table-changes: (26 commits)
  TableSync: try to make test more robust & add debug output
  Increase timeouts to see if FreeBSD will be happy with this.
  Try to make FreeBSD test happy with larger timeout.
  TableSync: refactor common functionality into function
  TableSync: don't raise &on_change, smaller fixes
  TableSync: rename auto_store -> table_store
  SyncTables: address feedback part 1 - naming (broker and zeek)
  BrokerStore <-> Zeek Tables: cleanup and bug workaround
  Zeek Table<->Brokerstore: cleanup, documentation, small fixes
  BrokerStore<->Zeek table: adopt to recent Zeek API changes
  BrokerStore<->Zeek Tables Fix a few small test failures.
  BrokerStore<->Zeek tables: allow setting storage location & tests
  BrokerStore<->Zeek tables: &backend works for in-memory stores.
  BrokerStore<->Zeek table - introdude &backend attribute
  BrokerStore<->Zeek tables: test for clones synchronizing to a master
  BrokerStore<->Zeek tables: load persistent tables on startup.
  Brokerstore<->Tables: attribute conflicts
  Zeek/Brokerstore updates: expiration
  Zeek/Brokerstore updates: add test that includes updates from clones
  Zeek/Brokerstore updates: first working end-to-end test
  ...
This commit is contained in:
Robin Sommer 2020-07-21 14:54:46 +00:00
commit c3f4971eb2
50 changed files with 2290 additions and 78 deletions

View file

@ -37,6 +37,8 @@
#include "ID.h"
#include "broker/Data.h"
#include "broker/Store.h"
#include "broker/Manager.h"
#include "threading/formatters/JSON.h"
@ -1511,6 +1513,16 @@ void TableVal::SetAttrs(detail::AttributesPtr a)
if ( cf )
change_func = cf->GetExpr();
auto bs = attrs->Find(zeek::detail::ATTR_BROKER_STORE);
if ( bs && broker_store.empty() )
{
IntrusivePtr<Val> c = bs->GetExpr()->Eval(nullptr);
assert(c);
assert(c->GetType()->Tag() == zeek::TYPE_STRING);
broker_store = c->AsStringVal()->AsString()->CheckString();
broker_mgr->AddForwardedStore(broker_store, {NewRef{}, this});
}
}
void TableVal::CheckExpireAttr(detail::AttrTag at)
@ -1539,7 +1551,7 @@ void TableVal::CheckExpireAttr(detail::AttrTag at)
}
}
bool TableVal::Assign(ValPtr index, ValPtr new_val)
bool TableVal::Assign(ValPtr index, ValPtr new_val, bool broker_forward)
{
auto k = MakeHashKey(*index);
@ -1549,7 +1561,7 @@ bool TableVal::Assign(ValPtr index, ValPtr new_val)
return false;
}
return Assign(std::move(index), std::move(k), std::move(new_val));
return Assign(std::move(index), std::move(k), std::move(new_val), broker_forward);
}
bool TableVal::Assign(Val* index, Val* new_val)
@ -1558,7 +1570,7 @@ bool TableVal::Assign(Val* index, Val* new_val)
}
bool TableVal::Assign(ValPtr index, std::unique_ptr<HashKey> k,
ValPtr new_val)
ValPtr new_val, bool broker_forward)
{
bool is_set = table_type->IsSet();
@ -1591,11 +1603,19 @@ bool TableVal::Assign(ValPtr index, std::unique_ptr<HashKey> k,
Modified();
if ( change_func )
if ( change_func || ( broker_forward && ! broker_store.empty() ) )
{
auto change_index = index ? std::move(index) : RecreateIndex(k_copy);
const auto& v = old_entry_val ? old_entry_val->GetVal() : new_entry_val->GetVal();
CallChangeFunc(change_index.get(), v, old_entry_val ? ELEMENT_CHANGED : ELEMENT_NEW);
auto change_index = index ? std::move(index)
: RecreateIndex(k_copy);
if ( broker_forward && ! broker_store.empty() )
SendToStore(change_index.get(), new_entry_val, old_entry_val ? ELEMENT_CHANGED : ELEMENT_NEW);
if ( change_func )
{
const auto& v = old_entry_val ? old_entry_val->GetVal() : new_entry_val->GetVal();
CallChangeFunc(change_index, v, old_entry_val ? ELEMENT_CHANGED : ELEMENT_NEW);
}
}
delete old_entry_val;
@ -2061,7 +2081,7 @@ ListValPtr TableVal::RecreateIndex(const HashKey& k) const
return table_hash->RecoverVals(k);
}
void TableVal::CallChangeFunc(const Val* index,
void TableVal::CallChangeFunc(const ValPtr& index,
const ValPtr& old_value,
OnChangeType tpe)
{
@ -2076,9 +2096,7 @@ void TableVal::CallChangeFunc(const Val* index,
auto thefunc = change_func->Eval(nullptr);
if ( ! thefunc )
{
return;
}
if ( thefunc->GetType()->Tag() != TYPE_FUNC )
{
@ -2087,10 +2105,14 @@ void TableVal::CallChangeFunc(const Val* index,
}
const zeek::Func* f = thefunc->AsFunc();
auto lv = index->AsListVal();
zeek::Args vl;
// we either get passed the raw index_val - or a ListVal with exactly one element.
if ( index->GetType()->Tag() == zeek::TYPE_LIST )
vl.reserve(2 + index->AsListVal()->Length() + table_type->IsTable());
else
vl.reserve(3 + table_type->IsTable());
Args vl;
vl.reserve(2 + lv->Length() + table_type->IsTable());
vl.emplace_back(NewRef{}, this);
switch ( tpe )
@ -2108,8 +2130,14 @@ void TableVal::CallChangeFunc(const Val* index,
vl.emplace_back(BifType::Enum::TableChange->GetEnumVal(BifEnum::TableChange::TABLE_ELEMENT_EXPIRED));
}
for ( const auto& v : lv->Vals() )
vl.emplace_back(v);
if ( index->GetType()->Tag() == zeek::TYPE_LIST )
{
for ( const auto& v : index->AsListVal()->Vals() )
vl.emplace_back(v);
}
else
vl.emplace_back(index);
if ( table_type->IsTable() )
vl.emplace_back(old_value);
@ -2124,9 +2152,115 @@ void TableVal::CallChangeFunc(const Val* index,
in_change_func = false;
}
ValPtr TableVal::Remove(const Val& index)
void TableVal::SendToStore(const Val* index, const TableEntryVal* new_entry_val, OnChangeType tpe)
{
if ( broker_store.empty() || ! index )
return;
try
{
auto handle = broker_mgr->LookupStore(broker_store);
if ( ! handle )
return;
// we either get passed the raw index_val - or a ListVal with exactly one element.
// Since Broker does not support ListVals, we have to unoll this in the second case.
const Val* index_val;
if ( index->GetType()->Tag() == zeek::TYPE_LIST )
{
if ( index->AsListVal()->Length() != 1 )
{
zeek::emit_builtin_error("table with complex index not supported for &broker_store");
return;
}
index_val = index->AsListVal()->Idx(0).get();
}
else
{
index_val = index;
}
auto broker_index = bro_broker::val_to_data(index_val);
if ( ! broker_index )
{
zeek::emit_builtin_error("invalid Broker data conversation for table index");
return;
}
switch ( tpe )
{
case ELEMENT_NEW:
case ELEMENT_CHANGED:
{
broker::optional<broker::timespan> expiry;
auto expire_time = GetExpireTime();
if ( expire_time == 0 )
// Entry is set to immediately expire. Let's not forward it.
break;
if ( expire_time > 0 )
{
if ( attrs->Find(zeek::detail::ATTR_EXPIRE_CREATE) )
{
// for create expiry, we have to substract the already elapsed time from the expiry.
auto e = expire_time - (network_time - new_entry_val->ExpireAccessTime());
if ( e <= 0 )
// element already expired? Let's not insert it.
break;
expiry = bro_broker::convert_expiry(e);
}
else
expiry = bro_broker::convert_expiry(expire_time);
}
if ( table_type->IsSet() )
handle->store.put(std::move(*broker_index), broker::data(), expiry);
else
{
if ( ! new_entry_val )
{
zeek::emit_builtin_error("did not receive new value for Broker datastore send operation");
return;
}
auto new_value = new_entry_val->GetVal().get();
auto broker_val = bro_broker::val_to_data(new_value);
if ( ! broker_val )
{
zeek::emit_builtin_error("invalid Broker data conversation for table value");
return;
}
handle->store.put(std::move(*broker_index), std::move(*broker_val), expiry);
}
break;
}
case ELEMENT_REMOVED:
handle->store.erase(std::move(*broker_index));
break;
case ELEMENT_EXPIRED:
// we do nothing here. The Broker store does its own expiration - so the element
// should expire at about the same time.
break;
}
}
catch ( InterpreterException& e )
{
zeek::emit_builtin_error("The previous error was encountered while trying to resolve the &broker_store attribute of the set/table. Potentially the Broker::Store has not been initialized before being used.");
}
}
ValPtr TableVal::Remove(const Val& index, bool broker_forward)
{
auto k = MakeHashKey(index);
TableEntryVal* v = k ? AsNonConstTable()->RemoveEntry(k.get()) : nullptr;
ValPtr va;
@ -2140,8 +2274,15 @@ ValPtr TableVal::Remove(const Val& index)
Modified();
if ( broker_forward && ! broker_store.empty() )
SendToStore(&index, nullptr, ELEMENT_REMOVED);
if ( change_func )
CallChangeFunc(&index, va, ELEMENT_REMOVED);
{
// this is totally cheating around the fact that we need a Intrusive pointer.
IntrusivePtr<Val> changefunc_val = RecreateIndex(*(k.get()));
CallChangeFunc(changefunc_val, va, ELEMENT_REMOVED);
}
return va;
}
@ -2166,10 +2307,14 @@ ValPtr TableVal::Remove(const HashKey& k)
Modified();
if ( change_func && va )
if ( va && ( change_func || ! broker_store.empty() ) )
{
auto index = table_hash->RecoverVals(k);
CallChangeFunc(index.get(), va, ELEMENT_REMOVED);
if ( ! broker_store.empty() )
SendToStore(index.get(), nullptr, ELEMENT_REMOVED);
if ( change_func && va )
CallChangeFunc(index, va, ELEMENT_REMOVED);
}
return va;
@ -2473,7 +2618,8 @@ void TableVal::DoExpire(double t)
{
if ( ! idx )
idx = RecreateIndex(*k);
CallChangeFunc(idx.get(), v->GetVal(), ELEMENT_EXPIRED);
CallChangeFunc(idx, v->GetVal(), ELEMENT_EXPIRED);
}
delete v;