/*! Sets the new component view. * The same component message should be injected in the queue separately * (see gcs_dummy_inject_msg()) in order to model different race conditions */ long gcs_dummy_set_component (gcs_backend_t* backend, const gcs_comp_msg_t* comp) { dummy_t* dummy = backend->conn; long new_num = gcs_comp_msg_num (comp); long i; assert (dummy->state > DUMMY_CLOSED); if (dummy->memb_num != new_num) { void* tmp = gu_realloc (dummy->memb, new_num * sizeof(gcs_comp_memb_t)); if (NULL == tmp) return -ENOMEM; dummy->memb = tmp; dummy->memb_num = new_num; } for (i = 0; i < dummy->memb_num; i++) { strcpy ((char*)&dummy->memb[i], gcs_comp_msg_id (comp, i)); } dummy->my_idx = gcs_comp_msg_self(comp); dummy->state = gcs_comp_msg_primary(comp) ? DUMMY_PRIM : DUMMY_NON_PRIM; gu_debug ("Setting state to %s", DUMMY_PRIM == dummy->state ? "DUMMY_PRIM" : "DUMMY_NON_PRIM"); return 0; }
static bool DUMMY_INSTALL_COMPONENT (gcs_backend_t* backend, const gcs_comp_msg_t* comp) { bool primary = gcs_comp_msg_primary (comp); long my_idx = gcs_comp_msg_self (comp); long members = gcs_comp_msg_num (comp); action_t act; FAIL_IF (gcs_dummy_set_component(Backend, comp), "", NULL); FAIL_IF (DUMMY_INJECT_COMPONENT (Backend, comp), "", NULL); FAIL_IF (CORE_RECV_ACT (&act, NULL, UNKNOWN_SIZE, GCS_ACT_CONF), "", NULL); FAIL_IF (core_test_check_conf(act.out, primary, my_idx, members), "", NULL); free (act.out); return false; }
gcs_group_state_t gcs_group_handle_comp_msg (gcs_group_t* group, const gcs_comp_msg_t* comp) { long new_idx, old_idx; gcs_node_t* new_nodes = NULL; ulong new_memb = 0; const bool prim_comp = gcs_comp_msg_primary (comp); const bool bootstrap = gcs_comp_msg_bootstrap(comp); const long new_my_idx = gcs_comp_msg_self (comp); const long new_nodes_num = gcs_comp_msg_num (comp); group_check_comp_msg (prim_comp, new_my_idx, new_nodes_num); if (new_my_idx >= 0) { gu_info ("New COMPONENT: primary = %s, bootstrap = %s, my_idx = %ld, " "memb_num = %ld", prim_comp ? "yes" : "no", bootstrap ? "yes" : "no", new_my_idx, new_nodes_num); new_nodes = group_nodes_init (group, comp); if (!new_nodes) { gu_fatal ("Could not allocate memory for %ld-node component.", gcs_comp_msg_num (comp)); assert(0); return (gcs_group_state_t)-ENOMEM; } if (GCS_GROUP_PRIMARY == group->state) { gu_debug ("#281: Saving %s over %s", gcs_node_state_to_str(group->nodes[group->my_idx].status), gcs_node_state_to_str(group->prim_state)); group->prim_state = group->nodes[group->my_idx].status; } } else { // Self-leave message gu_info ("Received self-leave message."); assert (0 == new_nodes_num); assert (!prim_comp); } if (prim_comp) { /* Got PRIMARY COMPONENT - Hooray! */ assert (new_my_idx >= 0); if (group->state == GCS_GROUP_PRIMARY) { /* we come from previous primary configuration, relax */ } else if (bootstrap) { /* Is there need to initialize something else in this case? */ group->nodes[group->my_idx].bootstrap = true; } else { const bool first_component = #ifndef GCS_CORE_TESTING (1 == group->num) && !strcmp (NODE_NO_ID, group->nodes[0].id); #else (1 == group->num); #endif if (1 == new_nodes_num && first_component) { /* bootstrap new configuration */ assert (GCS_GROUP_NON_PRIMARY == group->state); assert (1 == group->num); assert (0 == group->my_idx); // This bootstraps initial primary component for state exchange gu_uuid_generate (&group->prim_uuid, NULL, 0); group->prim_seqno = 0; group->prim_num = 1; group->state = GCS_GROUP_PRIMARY; if (group->act_id < 0) { // no history provided: start a new one group->act_id = GCS_SEQNO_NIL; gu_uuid_generate (&group->group_uuid, NULL, 0); gu_info ("Starting new group from scratch: "GU_UUID_FORMAT, GU_UUID_ARGS(&group->group_uuid)); } // the following should be removed under #474 group->nodes[0].status = GCS_NODE_STATE_JOINED; /* initialize node ID to the one given by the backend - this way * we'll be recognized as coming from prev. conf. in node array * remap below */ strncpy ((char*)group->nodes[0].id, new_nodes[0].id, sizeof (new_nodes[0].id) - 1); group->nodes[0].segment = new_nodes[0].segment; } } } else { group_go_non_primary (group); } /* Remap old node array to new one to preserve action continuity */ for (new_idx = 0; new_idx < new_nodes_num; new_idx++) { /* find member index in old component by unique member id */ for (old_idx = 0; old_idx < group->num; old_idx++) { // just scan through old group if (!strcmp(group->nodes[old_idx].id, new_nodes[new_idx].id)) { /* the node was in previous configuration with us */ /* move node context to new node array */ gcs_node_move (&new_nodes[new_idx], &group->nodes[old_idx]); break; } } /* if wasn't found in new configuration, new member - * need to do state exchange */ new_memb |= (old_idx == group->num); } /* free old nodes array */ group_nodes_free (group); group->my_idx = new_my_idx; group->num = new_nodes_num; group->nodes = new_nodes; if (gcs_comp_msg_primary(comp) || bootstrap) { /* TODO: for now pretend that we always have new nodes and perform * state exchange because old states can carry outdated node status. * (also protocol voting needs to be redone) * However this means aborting ongoing actions. Find a way to avoid * this extra state exchange. Generate new state messages on behalf * of other nodes? see #238 */ new_memb = true; /* if new nodes joined, reset ongoing actions and state messages */ if (new_memb) { group_nodes_reset (group); group->state = GCS_GROUP_WAIT_STATE_UUID; group->state_uuid = GU_UUID_NIL; // prepare for state exchange } else { if (GCS_GROUP_PRIMARY == group->state) { /* since we don't have any new nodes since last PRIMARY, we skip state exchange */ group_post_state_exchange (group); } } group_redo_last_applied (group); } return group->state; }