void DistributedObject::handle_ai_change(channel_t new_ai, channel_t sender, bool channel_is_explicit) { channel_t old_ai = m_ai_channel; if(new_ai == old_ai) { return; } // Set of channels that must be notified about ai_change unordered_set<channel_t> targets; if(old_ai) { targets.insert(old_ai); } if(!m_zone_objects.empty()) { // We have at least one child, so we want to notify the children as well targets.insert(parent_to_children(m_do_id)); } m_ai_channel = new_ai; m_ai_explicitly_set = channel_is_explicit; DatagramPtr dg = Datagram::create(targets, sender, STATESERVER_OBJECT_CHANGING_AI); dg->add_doid(m_do_id); dg->add_channel(new_ai); dg->add_channel(old_ai); route_datagram(dg); if(new_ai) { m_log->trace() << "Sending AI entry to " << new_ai << ".\n"; send_ai_entry(new_ai); } }
void DBStateServer::handle_get_fields_resp(DatagramIterator& dgi) { uint32_t db_context = dgi.read_uint32(); if(!is_expected_context(db_context)) { return; } // Get the datagram from the db_context DatagramPtr dg = m_context_datagrams[db_context]; m_context_datagrams.erase(db_context); // Check to make sure the datagram is appropriate DatagramIterator check_dgi = DatagramIterator(dg); uint16_t resp_type = check_dgi.get_msg_type(); if(resp_type != STATESERVER_OBJECT_GET_FIELDS_RESP) { if(resp_type == STATESERVER_OBJECT_GET_FIELD_RESP) { m_log->warning() << "Received GetFieldResp, but expecting GetFieldsResp." << std::endl; } else if(resp_type == STATESERVER_OBJECT_GET_ALL_RESP) { m_log->warning() << "Received GetAllResp, but expecting GetFieldsResp." << std::endl; } return; } m_log->trace() << "Received GetFieldResp from database." << std::endl; // Add database field payload to response (don't know dclass, so must copy payload). if(dgi.read_bool() == true) { dgi.read_uint16(); // Discard field count dg->add_data(dgi.read_remainder()); } route_datagram(dg); }
void DistributedObject::wake_children() { DatagramPtr dg = Datagram::create(parent_to_children(m_do_id), m_do_id, STATESERVER_OBJECT_GET_LOCATION); dg->add_uint32(STATESERVER_CONTEXT_WAKE_CHILDREN); route_datagram(dg); }
void DBStateServer::handle_get_all(channel_t sender, DatagramIterator &dgi) { uint32_t r_context = dgi.read_uint32(); doid_t r_do_id = dgi.read_doid(); if(is_activated_object(r_do_id)) { return; } m_log->trace() << "Received GetAll for inactive object with id " << r_do_id << std::endl; // Get context for db query, and remember caller with it uint32_t db_context = m_next_context++; DatagramPtr resp_dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_ALL_RESP); resp_dg->add_uint32(r_context); resp_dg->add_doid(r_do_id); resp_dg->add_channel(INVALID_CHANNEL); // Location m_context_datagrams[db_context] = resp_dg; // Cache the do_id --> context in case we get a dbss_activate m_inactive_loads[r_do_id].insert(db_context); // Send query to database DatagramPtr dg = Datagram::create(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_ALL); dg->add_uint32(db_context); dg->add_doid(r_do_id); route_datagram(dg); }
void Client::annihilate() { lock_guard<recursive_mutex> lock(m_client_lock); if(is_terminated()) { return; } // Unsubscribe from all channels first so the DELETE messages aren't sent back to us. unsubscribe_all(); m_client_agent->m_ct.free_channel(m_allocated_channel); // Delete all session objects while(m_session_objects.size() > 0) { doid_t do_id = *m_session_objects.begin(); m_session_objects.erase(do_id); m_log->debug() << "Client exited, deleting session object with id " << do_id << ".\n"; DatagramPtr dg = Datagram::create(do_id, m_channel, STATESERVER_OBJECT_DELETE_RAM); dg->add_doid(do_id); route_datagram(dg); } // Note that finish() normally results in the InterestOperation deleting // itself from m_pending_interests, so we have to be VERY careful with this // for loop. Using (it++) ensures that 'it' is advanced BEFORE finish() is // called; doing so after means the iterator is invalid! for(auto it = m_pending_interests.begin(); it != m_pending_interests.end();) { (it++)->second->finish(); } // Tell the MD this client is gone terminate(); }
void LoadingObject::send_get_object(doid_t do_id) { DatagramPtr dg = Datagram::create(m_dbss->m_db_channel, do_id, DBSERVER_OBJECT_GET_ALL); dg->add_uint32(m_context); // Context dg->add_doid(do_id); route_datagram(dg); }
void MessageDirector::recall_post_removes(channel_t sender) { // Clear post removes upstream if(m_upstream != nullptr) { DatagramPtr dg = Datagram::create(CONTROL_CLEAR_POST_REMOVES); dg->add_channel(sender); m_upstream->handle_datagram(dg); } }
void DBOperationGet::on_complete(DBObjectSnapshot *snapshot) { DatagramPtr resp = Datagram::create(); resp->add_server_header(m_sender, m_dbserver->m_control_channel, m_resp_msgtype); resp->add_uint32(m_context); resp->add_uint8(SUCCESS); // Calculate the fields that we are sending in our response: FieldValues response_fields; if(m_resp_msgtype == DBSERVER_OBJECT_GET_ALL_RESP) { // Send everything: response_fields = snapshot->m_fields; } else { // Send only what was requested: for(auto it = m_get_fields.begin(); it != m_get_fields.end(); ++it) { auto it2 = snapshot->m_fields.find(*it); if(it2 != snapshot->m_fields.end()) { response_fields[it2->first] = it2->second; } } } // WHAT we send depends on our m_resp_msgtype, so: if(m_resp_msgtype == DBSERVER_OBJECT_GET_FIELD_RESP) { if(response_fields.empty()) { // We did not find the field we were looking for. // Therefore, this is a failure. on_failure(); return; } resp->add_uint16(response_fields.begin()->first->get_id()); resp->add_data(response_fields.begin()->second); // And that's it. We're done. m_dbserver->route_datagram(resp); delete snapshot; cleanup(); return; } if(m_resp_msgtype == DBSERVER_OBJECT_GET_ALL_RESP) { resp->add_uint16(snapshot->m_dclass->get_id()); } resp->add_uint16(response_fields.size()); for(auto it = response_fields.begin(); it != response_fields.end(); ++it) { resp->add_uint16(it->first->get_id()); resp->add_data(it->second); } m_dbserver->route_datagram(resp); delete snapshot; cleanup(); }
void DistributedObject::delete_children(channel_t sender) { if(!m_zone_objects.empty()) { // We have at least one child, so we want to notify the children as well DatagramPtr dg = Datagram::create(parent_to_children(m_do_id), sender, STATESERVER_OBJECT_DELETE_CHILDREN); dg->add_doid(m_do_id); route_datagram(dg); } }
void MessageDirector::preroute_post_remove(channel_t sender, DatagramHandle post_remove) { // Add post remove upstream if(m_upstream != nullptr) { DatagramPtr dg = Datagram::create(CONTROL_ADD_POST_REMOVE); dg->add_channel(sender); dg->add_blob(post_remove); m_upstream->handle_datagram(dg); } }
bool DistributedObject::handle_one_update(DatagramIterator &dgi, channel_t sender) { vector<uint8_t> data; uint16_t field_id = dgi.read_uint16(); const Field *field = m_dclass->get_field_by_id(field_id); if(!field) { m_log->error() << "Received set_field for field: " << field_id << ", not valid for class: " << m_dclass->get_name() << ".\n"; return false; } m_log->trace() << "Handling update for '" << field->get_name() << "'.\n"; dgsize_t field_start = dgi.tell(); try { dgi.unpack_field(field, data); } catch(const DatagramIteratorEOF&) { m_log->error() << "Received truncated update for " << field->get_name() << ".\n"; return false; } const MolecularField *molecular = field->as_molecular(); if(molecular) { dgi.seek(field_start); int n = molecular->get_num_fields(); for(int i = 0; i < n; ++i) { vector<uint8_t> field_data; const Field *atomic = molecular->get_field(i); dgi.unpack_field(atomic, field_data); save_field(atomic, field_data); } } else { save_field(field, data); } unordered_set<channel_t> targets; if(field->has_keyword("broadcast")) { targets.insert(location_as_channel(m_parent_id, m_zone_id)); } if(field->has_keyword("airecv") && m_ai_channel && m_ai_channel != sender) { targets.insert(m_ai_channel); } if(field->has_keyword("ownrecv") && m_owner_channel && m_owner_channel != sender) { targets.insert(m_owner_channel); } if(targets.size()) { // TODO: Review this for efficiency? DatagramPtr dg = Datagram::create(targets, sender, STATESERVER_OBJECT_SET_FIELD); dg->add_doid(m_do_id); dg->add_uint16(field_id); dg->add_data(data); route_datagram(dg); } return true; }
void DBOperationUpdate::on_failure() { DatagramPtr resp = Datagram::create(); resp->add_server_header(m_sender, m_dbserver->m_control_channel, m_resp_msgtype); resp->add_uint32(m_context); resp->add_uint8(FAILURE); m_dbserver->route_datagram(resp); cleanup(); }
void DBOperationDelete::on_complete() { // Broadcast update to object's channel if(m_dbserver->m_broadcast) { DatagramPtr update = Datagram::create(); update->add_server_header(database_to_object(m_doid), m_sender, DBSERVER_OBJECT_DELETE); update->add_doid(m_doid); m_dbserver->route_datagram(update); } cleanup(); }
void DBOperationCreate::on_complete(doid_t doid) { DatagramPtr resp = Datagram::create(); resp->add_server_header(m_sender, m_dbserver->m_control_channel, DBSERVER_CREATE_OBJECT_RESP); resp->add_uint32(m_context); resp->add_doid(doid); m_dbserver->route_datagram(resp); cleanup(); }
// notify_interest_done send a CLIENTAGENT_DONE_INTEREST_RESP to the // interest operation's caller, if one has been set. void Client::notify_interest_done(uint16_t interest_id, channel_t caller) { if(caller == 0) { return; } DatagramPtr resp = Datagram::create(caller, m_channel, CLIENTAGENT_DONE_INTEREST_RESP); resp->add_channel(m_channel); resp->add_uint16(interest_id); route_datagram(resp); }
// notify_interest_done send a CLIENTAGENT_DONE_INTEREST_RESP to the // interest operation's caller, if one has been set. void Client::notify_interest_done(const InterestOperation* iop) { if(iop->m_callers.size() == 0) { return; } DatagramPtr resp = Datagram::create(iop->m_callers, m_channel, CLIENTAGENT_DONE_INTEREST_RESP); resp->add_channel(m_channel); resp->add_uint16(iop->m_interest_id); route_datagram(resp); }
void DistributedObject::send_interest_entry(channel_t location, uint32_t context) { DatagramPtr dg = Datagram::create(location, m_do_id, m_ram_fields.size() ? STATESERVER_OBJECT_ENTER_INTEREST_WITH_REQUIRED_OTHER : STATESERVER_OBJECT_ENTER_INTEREST_WITH_REQUIRED); dg->add_uint32(context); append_required_data(dg, true); if(m_ram_fields.size()) { append_other_data(dg, true); } route_datagram(dg); }
void handle_create(DBClientBase *client, DBOperation *operation) { // First, let's convert the requested object into BSON; this way, if // a failure happens, it happens before we waste a doid. BSONObjBuilder fields; try { for(auto it = operation->set_fields().begin(); it != operation->set_fields().end(); ++it) { DatagramPtr dg = Datagram::create(); dg->add_data(it->second); DatagramIterator dgi(dg); fields << it->first->get_name() << bamboo2bson(it->first->get_type(), dgi)["_"]; } } catch(mongo::DBException &e) { m_log->error() << "While formatting " << operation->dclass()->get_name() << " for insertion: " << e.what() << endl; operation->on_failure(); return; } doid_t doid = assign_doid(client); if(doid == INVALID_DO_ID) { // The error will already have been emitted at this point, so // all that's left for us to do is fail silently: operation->on_failure(); return; } BSONObj b = BSON("_id" << doid << "dclass" << operation->dclass()->get_name() << "fields" << fields.obj()); m_log->trace() << "Inserting new " << operation->dclass()->get_name() << "(" << doid << "): " << b << endl; try { client->insert(m_obj_collection, b); } catch(mongo::DBException &e) { m_log->error() << "Cannot insert new " << operation->dclass()->get_name() << "(" << doid << "): " << e.what() << endl; operation->on_failure(); return; } operation->on_complete(doid); }
void DBStateServer::handle_get_activated(channel_t sender, DatagramIterator& dgi) { uint32_t r_context = dgi.read_uint32(); doid_t r_do_id = dgi.read_doid(); if(m_loading.find(r_do_id) != m_loading.end()) { return; } m_log->trace() << "Received GetActivated for id " << r_do_id << "\n"; if(m_objs.find(r_do_id) != m_objs.end()) { // If object is active return true DatagramPtr dg = Datagram::create(sender, r_do_id, DBSS_OBJECT_GET_ACTIVATED_RESP); dg->add_uint32(r_context); dg->add_doid(r_do_id); dg->add_bool(true); route_datagram(dg); } else { // If object isn't active or loading, we can return false DatagramPtr dg = Datagram::create(sender, r_do_id, DBSS_OBJECT_GET_ACTIVATED_RESP); dg->add_uint32(r_context); dg->add_doid(r_do_id); dg->add_bool(false); route_datagram(dg); } }
void DistributedObject::append_required_data(DatagramPtr dg, bool client_only, bool also_owner) { dg->add_doid(m_do_id); dg->add_location(m_parent_id, m_zone_id); dg->add_uint16(m_dclass->get_id()); size_t field_count = m_dclass->get_num_fields(); for(size_t i = 0; i < field_count; ++i) { const Field *field = m_dclass->get_field(i); if(field->has_keyword("required") && !field->as_molecular() && (!client_only || field->has_keyword("broadcast") || field->has_keyword("clrecv") || (also_owner && field->has_keyword("ownrecv")))) { dg->add_data(m_required_fields[field]); } } }
void StateServer::handle_delete_ai(DatagramIterator& dgi, channel_t sender) { channel_t ai_channel = dgi.read_channel(); std::set <channel_t> targets; for(auto it = m_objs.begin(); it != m_objs.end(); ++it) { if(it->second && it->second->m_ai_channel == ai_channel && it->second->m_ai_explicitly_set) { targets.insert(it->second->m_do_id); } } if(targets.size()) { DatagramPtr dg = Datagram::create(targets, sender, STATESERVER_DELETE_AI_OBJECTS); dg->add_channel(ai_channel); route_datagram(dg); } }
void DBOperationUpdate::on_complete() { // Broadcast update to object's channel if(m_dbserver->m_broadcast) { announce_fields(m_set_fields); } // Send update response DatagramPtr resp = Datagram::create(); resp->add_server_header(m_sender, m_dbserver->m_control_channel, m_resp_msgtype); resp->add_uint32(m_context); resp->add_uint8(SUCCESS); m_dbserver->route_datagram(resp); cleanup(); }
Client::~Client() { unsubscribe_all(); m_client_agent->m_ct.free_channel(m_allocated_channel); // Delete all session objects while(m_session_objects.size() > 0) { doid_t do_id = *m_session_objects.begin(); m_session_objects.erase(do_id); m_log->debug() << "Client exited, deleting session object with id " << do_id << ".\n"; DatagramPtr dg = Datagram::create(do_id, m_channel, STATESERVER_OBJECT_DELETE_RAM); dg->add_doid(do_id); route_datagram(dg); } delete m_log; }
void DBOperationUpdate::on_criteria_mismatch(DBObjectSnapshot *snapshot) { DatagramPtr resp = Datagram::create(); resp->add_server_header(m_sender, m_dbserver->m_control_channel, m_resp_msgtype); resp->add_uint32(m_context); resp->add_uint8(FAILURE); // Calculate the fields that we are sending in our response: FieldValues mismatched_fields; for(auto it = m_criteria_fields.begin(); it != m_criteria_fields.end(); ++it) { auto it2 = snapshot->m_fields.find(it->first); if(it2 != snapshot->m_fields.end() && !it2->second.empty()) { mismatched_fields[it2->first] = it2->second; } } if(m_resp_msgtype == DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP) { resp->add_uint16(mismatched_fields.size()); } for(auto it = mismatched_fields.begin(); it != mismatched_fields.end(); ++it) { resp->add_uint16(it->first->get_id()); resp->add_data(it->second); } m_dbserver->route_datagram(resp); delete snapshot; cleanup(); }
// Get a DBObjectSnapshot from a MongoDB BSON object; returns NULL if failure. DBObjectSnapshot *format_snapshot(doid_t doid, const BSONObj &obj) { m_log->trace() << "Formatting database snapshot of " << doid << ": " << obj << endl; try { string dclass_name = obj["dclass"].String(); const dclass::Class *dclass = g_dcf->get_class_by_name(dclass_name); if(!dclass) { m_log->error() << "Encountered unknown database object: " << dclass_name << "(" << doid << ")" << endl; return NULL; } BSONObj fields = obj["fields"].Obj(); DBObjectSnapshot *snap = new DBObjectSnapshot(); snap->m_dclass = dclass; for(auto it = fields.begin(); it.more(); ++it) { const char *name = (*it).fieldName(); const dclass::Field *field = dclass->get_field_by_name(name); if(!field) { m_log->warning() << "Encountered unexpected field " << name << " while formatting " << dclass_name << "(" << doid << "); ignored." << endl; continue; } { DatagramPtr dg = Datagram::create(); bson2bamboo(field->get_type(), *it, *dg); snap->m_fields[field].resize(dg->size()); memcpy(snap->m_fields[field].data(), dg->get_data(), dg->size()); } } return snap; } catch(mongo::DBException &e) { m_log->error() << "Unexpected error while trying to format" " database snapshot for " << doid << ": " << e.what() << endl; return NULL; } }
void DBStateServer::handle_delete_disk(channel_t sender, DatagramIterator& dgi) { doid_t do_id = dgi.read_doid(); if(m_loading.find(do_id) != m_loading.end()) { // Ignore this message for now, it'll be bounced back to us // from the loading object if it succeeds or fails at loading. return; } // If object exists broadcast the delete message auto obj_keyval = m_objs.find(do_id); if(obj_keyval != m_objs.end()) { DistributedObject* obj = obj_keyval->second; std::unordered_set<channel_t> targets; // Add location to broadcast if(obj->get_location()) { targets.insert(obj->get_location()); } // Add AI to broadcast if(obj->get_ai()) { targets.insert(obj->get_ai()); } // Add owner to broadcast if(obj->get_owner()) { targets.insert(obj->get_owner()); } // Build and send datagram DatagramPtr dg = Datagram::create(targets, sender, DBSS_OBJECT_DELETE_DISK); dg->add_doid(do_id); route_datagram(dg); } // Send delete to database DatagramPtr dg = Datagram::create(m_db_channel, do_id, DBSERVER_OBJECT_DELETE); dg->add_doid(do_id); route_datagram(dg); }
void DistributedObject::annihilate(channel_t sender, bool notify_parent) { unordered_set<channel_t> targets; if(m_parent_id) { targets.insert(location_as_channel(m_parent_id, m_zone_id)); // Leave parent on explicit delete ram if(notify_parent) { DatagramPtr dg = Datagram::create(m_parent_id, sender, STATESERVER_OBJECT_CHANGING_LOCATION); dg->add_doid(m_do_id); dg->add_location(INVALID_DO_ID, 0); dg->add_location(m_parent_id, m_zone_id); route_datagram(dg); } } if(m_owner_channel) { targets.insert(m_owner_channel); } if(m_ai_channel) { targets.insert(m_ai_channel); } DatagramPtr dg = Datagram::create(targets, sender, STATESERVER_OBJECT_DELETE_RAM); dg->add_doid(m_do_id); route_datagram(dg); delete_children(sender); m_stateserver->m_objs.erase(m_do_id); m_log->debug() << "Deleted.\n"; terminate(); }
void DBStateServer::handle_set_fields(DatagramIterator &dgi) { doid_t do_id = dgi.read_doid(); if(m_loading.find(do_id) != m_loading.end()) { // Ignore this message for now, it'll be bounced back to us // from the loading object if it succeeds or fails at loading. return; } uint16_t field_count = dgi.read_uint16(); FieldValues db_fields; for(uint16_t i = 0; i < field_count; ++i) { uint16_t field_id = dgi.read_uint16(); const Field* field = g_dcf->get_field_by_id(field_id); if(!field) { m_log->warning() << "Received invalid field with id " << field_id << " in SetFields.\n"; return; } if(field->has_keyword("db")) { dgi.unpack_field(field, db_fields[field]); } else { dgi.skip_field(field); } } if(db_fields.size() > 0) { m_log->trace() << "Forwarding SetFields on object with id " << do_id << " to database.\n"; DatagramPtr dg = Datagram::create(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELDS); dg->add_doid(do_id); dg->add_uint16(db_fields.size()); for(const auto& it : db_fields) { dg->add_uint16(it.first->get_id()); dg->add_data(it.second); } route_datagram(dg); } }
bool DistributedObject::handle_one_get(DatagramPtr out, uint16_t field_id, bool succeed_if_unset, bool is_subfield) { const Field *field = m_dclass->get_field_by_id(field_id); if(!field) { m_log->error() << "Received get_field for field: " << field_id << ", not valid for class: " << m_dclass->get_name() << ".\n"; return false; } m_log->trace() << "Handling query for '" << field->get_name() << "'.\n"; const MolecularField *molecular = field->as_molecular(); if(molecular) { int n = molecular->get_num_fields(); out->add_uint16(field_id); for(int i = 0; i < n; ++i) { if(!handle_one_get(out, molecular->get_field(i)->get_id(), succeed_if_unset, true)) { return false; } } return true; } if(m_required_fields.count(field)) { if(!is_subfield) { out->add_uint16(field_id); } out->add_data(m_required_fields[field]); } else if(m_ram_fields.count(field)) { if(!is_subfield) { out->add_uint16(field_id); } out->add_data(m_ram_fields[field]); } else { return succeed_if_unset; } return true; }
void DBStateServer::handle_set_field(DatagramIterator &dgi) { doid_t do_id = dgi.read_doid(); if(m_loading.find(do_id) != m_loading.end()) { // Ignore this message for now, it'll be bounced back to us // from the loading object if it succeeds or fails at loading. return; } uint16_t field_id = dgi.read_uint16(); const Field* field = g_dcf->get_field_by_id(field_id); if(field && field->has_keyword("db")) { m_log->trace() << "Forwarding SetField for field \"" << field->get_name() << "\" on object with id " << do_id << " to database.\n"; DatagramPtr dg = Datagram::create(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELD); dg->add_doid(do_id); dg->add_uint16(field_id); dg->add_data(dgi.read_remainder()); route_datagram(dg); } }