Ejemplo n.º 1
0
def _setup_replication(shard_id, source_group_id, destn_group_id, split_value,
                                        prune_limit, cmd):
    """Setup replication between the source and the destination groups and
    ensure that they are in sync.

    :param shard_id: The shard ID of the shard that needs to be moved.
    :param source_group_id: The group_id of the source shard.
    :param destn_group_id: The ID of the group to which the shard needs to
                           be moved.
    :param split_value: Indicates the value at which the range for the
                        particular shard will be split. Will be set only
                        for shard split operations.
    :param prune_limit: The number of DELETEs that should be
                        done in one batch.
    :param cmd: Indicates the type of re-sharding operation
    """
    source_group = Group.fetch(source_group_id)
    if source_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (source_group_id, ))

    destination_group = Group.fetch(destn_group_id)
    if destination_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (destn_group_id, ))

    master = MySQLServer.fetch(source_group.master)
    if master is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    master.connect()

    slave = MySQLServer.fetch(destination_group.master)
    if slave is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    slave.connect()

    #Stop and reset any slave that  might be running on the slave server.
    _utils.set_offline_mode(slave, True) ### TODO: if forced offline_mode
    _replication.stop_slave(slave, wait=True)
    _replication.reset_slave(slave, clean=True)

    #Change the master to the shard group master.
    _replication.switch_master(slave, master, master.repl_user, master.repl_pass)

    #Start the slave so that syncing of the data begins
    _replication.start_slave(slave, wait=True)
    _utils.set_offline_mode(slave, False) ### TODO: if forced offline_mode

    #Setup sync between the source and the destination groups.
    _events.trigger_within_procedure(
                                     SETUP_SYNC,
                                     shard_id,
                                     source_group_id,
                                     destn_group_id,
                                     split_value,
                                     prune_limit,
                                     cmd
                                     )
Ejemplo n.º 2
0
def _setup_sync(shard_id, source_group_id, destn_group_id, split_value,
                                        prune_limit, cmd):

    """sync the source and the destination groups.

    :param shard_id: The shard ID of the shard that needs to be moved.
    :param source_group_id: The group_id of the source shard.
    :param destn_group_id: The ID of the group to which the shard needs to
                           be moved.
    :param split_value: Indicates the value at which the range for the
                        particular shard will be split. Will be set only
                        for shard split operations.
    :param prune_limit: The number of DELETEs that should be
                        done in one batch.
    :param cmd: Indicates the type of re-sharding operation
    """
    source_group = Group.fetch(source_group_id)
    if source_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (source_group_id, ))

    destination_group = Group.fetch(destn_group_id)
    if destination_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (destn_group_id, ))

    master = MySQLServer.fetch(source_group.master)
    if master is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    master.connect()

    slave = MySQLServer.fetch(destination_group.master)
    if slave is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    slave.connect()

    #Synchronize until the slave catches up with the master.
    _replication.synchronize_with_read_only(slave, master)

    #Reset replication once the syncing is done.
    _replication.stop_slave(slave, wait=True)
    _replication.reset_slave(slave, clean=True)

    #Trigger changing the mappings for the shard that was copied
    _events.trigger_within_procedure(
                                     SETUP_RESHARDING_SWITCH,
                                     shard_id,
                                     source_group_id,
                                     destn_group_id,
                                     split_value,
                                     prune_limit,
                                     cmd
                                     )
Ejemplo n.º 3
0
def _lookup(lookup_arg, key, hint):
    """Given a table name and a key return the servers of the Group where the
    shard of this table can be found

    :param lookup_arg: table name for "LOCAL" lookups
                Shard Mapping ID for "GLOBAL" lookups.
    :param key: The key value that needs to be looked up
    :param hint: A hint indicates if the query is LOCAL or GLOBAL

    :return: The servers of the Group that contains the range in which the
            key belongs.
    """
    VALID_HINTS = ('LOCAL', 'GLOBAL')
    hint = hint.upper()
    if hint not in VALID_HINTS:
        raise _errors.ShardingError(INVALID_SHARDING_HINT)

    group = None

    #Perform the lookup for the group contaning the lookup data.
    if hint == "GLOBAL":
        #Fetch the shard mapping object. In the case of GLOBAL lookups
        #the shard mapping ID is passed directly. In the case of "LOCAL"
        #lookups it is the table name that is passed.
        shard_mapping = ShardMapping.fetch_by_id(lookup_arg)
        if shard_mapping is None:
            raise _errors.ShardingError(SHARD_MAPPING_NOT_FOUND %
                                        (lookup_arg, ))
        #GLOBAL lookups. There can be only one global group, hence using
        #shard_mapping[0] is safe.
        group_id = shard_mapping[0].global_group
    else:
        shard_mapping = ShardMapping.fetch(lookup_arg)
        if shard_mapping is None:
            raise _errors.ShardingError(TABLE_NAME_NOT_FOUND % (lookup_arg, ))
        sharding_specification = \
            SHARDING_SPECIFICATION_HANDLER[shard_mapping.type_name].\
            lookup(key, shard_mapping.shard_mapping_id, shard_mapping.type_name)
        if sharding_specification is None:
            raise _errors.ShardingError(INVALID_SHARDING_KEY % (key, ))
        shard = Shards.fetch(str(sharding_specification.shard_id))
        if shard.state == "DISABLED":
            raise _errors.ShardingError(SHARD_NOT_ENABLED)
        #group cannot be None since there is a foreign key on the group_id.
        #An exception will be thrown nevertheless.
        group_id = shard.group_id

    return ServerLookups().execute(group_id=group_id)
Ejemplo n.º 4
0
def fetch_backup_server(source_group):
    """Fetch a spare, slave or master from a group in that order of
    availability. Find any spare (no criteria), if there is no spare, find
    a secondary(no criteria) and if there is no secondary the master.

    :param source_group: The group from which the server needs to
                         be fetched.
    """
    #Get a slave server whose status is spare.
    backup_server = None
    for server in source_group.servers():
        if server.status == "SPARE":
            backup_server = server

    #If there is no spare check if a running slave is available
    if backup_server is None:
        for server in source_group.servers():
            if source_group.master != server.uuid and \
                server.status == "SECONDARY":
                backup_server = server

    #If there is no running slave just use the master
    if backup_server is None:
        backup_server = _server.MySQLServer.fetch(source_group.master)

    #If there is no master throw an exception
    if backup_server is None:
        raise _errors.ShardingError(GROUP_MASTER_NOT_FOUND)

    return backup_server
Ejemplo n.º 5
0
def _remove_shard(shard_id):
    """Remove the RANGE specification mapping represented by the current
    RANGE shard specification object.

    :param shard_id: The shard ID of the shard that needs to be removed.

    :return: True if the remove succeeded
            False if the query failed
    :raises: ShardingError if the shard id is not found,
        :       ShardingError if the shard is not disabled.
    """
    range_sharding_specification, shard, _, _ = \
        verify_and_fetch_shard(shard_id)
    if shard.state == "ENABLED":
        raise _errors.ShardingError(SHARD_NOT_DISABLED)
    #Stop the replication of the shard group with the global
    #group. Also clear the references of the master and the
    #slave group from the current group.
    #NOTE: When we do the stopping of the shard group
    #replication in shard remove we are actually just clearing
    #the references, since a shard cannot  be removed unless
    #it is disabled and when it is disabled the replication is
    #stopped but the references are not cleared.
    _stop_shard_group_replication(shard_id, True)
    range_sharding_specification.remove()
    shard.remove()
    _LOGGER.debug("Removed Shard (%s).", shard_id)
Ejemplo n.º 6
0
def _list(sharding_type):
    """The method returns all the shard mappings (names) of a
    particular sharding_type. For example if the method is called
    with 'range' it returns all the sharding specifications that exist
    of type range.

    :param sharding_type: The sharding type for which the sharding
                          specification needs to be returned.

    :return: A list of dictionaries of shard mappings that are of
                 the sharding type
                 An empty list of the sharding type is valid but no
                 shard mapping definition is found
                 An error if the sharding type is invalid.

    :raises: Sharding Error if Sharding type is not found.
    """
    if sharding_type not in Shards.VALID_SHARDING_TYPES:
        raise _errors.ShardingError(INVALID_SHARDING_TYPE % (sharding_type, ))

    ret_shard_mappings = []
    shard_mappings = ShardMapping.list(sharding_type)
    for shard_mapping in shard_mappings:
        ret_shard_mappings.append({
            "mapping_id": shard_mapping.shard_mapping_id,
            "table_name": shard_mapping.table_name,
            "column_name": shard_mapping.column_name,
            "type_name": shard_mapping.type_name,
            "global_group": shard_mapping.global_group
        })
    return ret_shard_mappings
Ejemplo n.º 7
0
def _restore_shard_backup(shard_id, source_group_id, destn_group_id,
                          backup_image, split_value, prune_limit, cmd):
    """Restore the backup on the destination Group.

    :param shard_id: The shard ID of the shard that needs to be moved.
    :param source_group_id: The group_id of the source shard.
    :param destn_group_id: The ID of the group to which the shard needs to
                           be moved.
    :param backup_image: The destination file that contains the backup
                         of the source shard.
    :param split_value: Indicates the value at which the range for the
                        particular shard will be split. Will be set only
                        for shard split operations.
    :param prune_limit: The number of DELETEs that should be
                        done in one batch.
    :param cmd: Indicates the type of re-sharding operation
    """
    restore_user = _services_utils.read_config_value(
                            _config.global_config,
                            'servers',
                            'restore_user'
                        )
    restore_passwd = _services_utils.read_config_value(
                            _config.global_config,
                            'servers',
                            'restore_password'
                        )
    mysqlclient_binary = _services_utils.read_config_value(
                            _config.global_config,
                            'sharding',
                            'mysqlclient_program'
                        )

    destn_group = Group.fetch(destn_group_id)
    if destn_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (destn_group_id, ))

    #Build a backup image that will be used for restoring
    bk_img = _backup.BackupImage(backup_image)

    for destn_group_server in destn_group.servers():
        destn_group_server.connect()
        _backup.MySQLDump.restore_fabric_server(
            destn_group_server,
            restore_user, restore_passwd,
            bk_img,
            mysqlclient_binary
        )

    #Setup sync between the source and the destination groups.
    _events.trigger_within_procedure(
                                     SETUP_REPLICATION,
                                     shard_id,
                                     source_group_id,
                                     destn_group_id,
                                     split_value,
                                     prune_limit,
                                     cmd
                                     )
Ejemplo n.º 8
0
    def split_value(lower_bound, upper_bound):
        """For a Range Sharding definition on DATETIME, the split value should,
        be explicitly defined.

        :param lower_bound: The lower bound of the shard range.
        :param upper_bound: The upper bound of the shard range.
        """
        raise _errors.ShardingError("The split value should be defined")
Ejemplo n.º 9
0
def verify_and_fetch_shard(shard_id):
    """Find out if the shard_id exists and return the sharding specification for
    it. If it does not exist throw an exception.

    :param shard_id: The ID for the shard whose specification needs to be
                     fetched.

    :return: The sharding specification class representing the shard ID.

    :raises: ShardingError if the shard ID is not found.
    """
    #Here the underlying sharding specification might be a RANGE
    #or a HASH. The type of sharding specification is obtained from the
    #shard mapping.
    range_sharding_spec = RangeShardingSpecification.fetch(shard_id)
    if range_sharding_spec is None:
        raise _errors.ShardingError(SHARD_NOT_FOUND % (shard_id, ))

    #Fetch the shard mappings and use them to find the type of sharding
    #scheme.
    shard_mappings = ShardMapping.fetch_by_id(
        range_sharding_spec.shard_mapping_id)
    if shard_mappings is None:
        raise _errors.ShardingError(SHARD_MAPPING_NOT_FOUND %
                                    (range_sharding_spec.shard_mapping_id, ))

    #Fetch the shard mapping definition. There is only one shard mapping
    #definition associated with all of the shard mappings.
    shard_mapping_defn = ShardMapping.fetch_shard_mapping_defn(
        range_sharding_spec.shard_mapping_id)
    if shard_mapping_defn is None:
        raise _errors.ShardingError(SHARD_MAPPING_DEFN_NOT_FOUND %
                                    (range_sharding_spec.shard_mapping_id, ))

    shard = Shards.fetch(shard_id)
    if shard is None:
        raise _errors.ShardingError(SHARD_NOT_FOUND % (shard_id, ))

    #Both of the shard_mappings retrieved will be of the same sharding
    #type. Hence it is safe to use one of them to retireve the sharding type.
    if shard_mappings[0].type_name == "HASH":
        return HashShardingSpecification.fetch(shard_id), \
            shard,  shard_mappings, shard_mapping_defn
    else:
        return range_sharding_spec, shard, shard_mappings, shard_mapping_defn
Ejemplo n.º 10
0
def _remove_shard_mapping(table_name):
    """Remove the shard mapping for the given table.

    :param table_name: The name of the table for which the shard mapping
                        needs to be removed.

    :return: True if the remove succeeded
            False if the query failed
    :raises: ShardingError if the table name is not found.
    """
    shard_mapping = ShardMapping.fetch(table_name)
    if shard_mapping is None:
        raise _errors.ShardingError(TABLE_NAME_NOT_FOUND % (table_name, ))
    shard_mapping.remove()
Ejemplo n.º 11
0
def _define_shard_mapping(type_name, global_group_id):
    """Define a shard mapping.

    :param type_name: The type of sharding scheme - RANGE, HASH, LIST etc
    :param global_group: Every shard mapping is associated with a
                        Global Group that stores the global updates
                        and the schema changes for this shard mapping
                        and dissipates these to the shards.
    :return: The shard_mapping_id generated for the shard mapping.
    :raises: ShardingError if the sharding type is invalid.
    """
    type_name = type_name.upper()
    if type_name not in Shards.VALID_SHARDING_TYPES:
        raise _errors.ShardingError(INVALID_SHARDING_TYPE % (type_name, ))
    shard_mapping_id = ShardMapping.define(type_name, global_group_id)
    return shard_mapping_id
Ejemplo n.º 12
0
def _setup_shard_switch_move(shard_id, source_group_id, destination_group_id,
                             update_only):
    """Setup the moved shard to map to the new group.

    :param shard_id: The shard ID of the shard that needs to be moved.
    :param source_group_id: The group_id of the source shard.
    :param destination_group_id: The ID of the group to which the shard
                                needs to be moved.
    :update_only: Only update the state store and skip provisioning.
    """
    #Fetch the Range sharding specification. When we start implementing
    #heterogenous sharding schemes, we need to find out the type of
    #sharding scheme and we should use that to find out the sharding
    #implementation.
    _, source_shard, _, shard_mapping_defn = \
        _services_sharding.verify_and_fetch_shard(shard_id)

    destination_group = Group.fetch(destination_group_id)
    if destination_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (destination_group_id, ))
    destn_group_master = MySQLServer.fetch(destination_group.master)
    if destn_group_master is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    destn_group_master.connect()
    #Set the destination group master to read_only
    destn_group_master.read_only = True

    #Setup replication between the shard group and the global group.
    _group_replication.setup_group_replication \
            (shard_mapping_defn[2], destination_group_id)
    #set the shard to point to the new group.
    source_shard.group_id = destination_group_id
    #Stop the replication between the global server and the original
    #group associated with the shard.
    _group_replication.stop_group_slave\
            (shard_mapping_defn[2], source_group_id,  True)

    #The sleep ensures that the connector have refreshed their caches with the
    #new shards that have been added as a result of the split.
    time.sleep(_utils.TTL)

    #Reset the read only flag on the source server.
    source_group = Group.fetch(source_group_id)
    if source_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (source_group_id, ))

    master = MySQLServer.fetch(source_group.master)
    if master is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)

    if not update_only:
        master.connect()
        master.read_only = False
        #Kill all the existing connections on the servers
        source_group.kill_connections_on_servers()
        #allow updates in the destination group master
        destn_group_master.read_only = False
Ejemplo n.º 13
0
def _setup_shard_switch_split(shard_id, source_group_id, destination_group_id,
                              split_value, prune_limit, cmd, update_only):
    """Setup the moved shard to map to the new group.

    :param shard_id: The shard ID of the shard that needs to be moved.
    :param source_group_id: The group_id of the source shard.
    :param destn_group_id: The ID of the group to which the shard needs to
                           be moved.
    :param split_value: Indicates the value at which the range for the
                        particular shard will be split. Will be set only
                        for shard split operations.
    :param prune_limit: The number of DELETEs that should be
                        done in one batch.
    :param cmd: Indicates the type of re-sharding operation.
    :update_only: Only update the state store and skip provisioning.
    """
    #Fetch the Range sharding specification.
    range_sharding_spec, source_shard, shard_mappings, shard_mapping_defn = \
            _services_sharding.verify_and_fetch_shard(shard_id)

    #Disable the old shard
    source_shard.disable()

    #Remove the old shard.
    range_sharding_spec.remove()
    source_shard.remove()

    destination_group = Group.fetch(destination_group_id)
    if destination_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (destination_group_id, ))
    destn_group_master = MySQLServer.fetch(destination_group.master)
    if destn_group_master is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    destn_group_master.connect()

    #Make the destination group as read only to disable updates until the
    #connectors update their caches, thus avoiding inconsistency.
    destn_group_master.read_only = True

    #Add the new shards. Generate new shard IDs for the shard being
    #split and also for the shard that is created as a result of the split.
    new_shard_1 = Shards.add(source_shard.group_id, "DISABLED")
    new_shard_2 = Shards.add(destination_group_id, "DISABLED")

    #Both of the shard mappings associated with this shard_id should
    #be of the same sharding type. Hence it is safe to use one of the
    #shard mappings.
    if shard_mappings[0].type_name == "HASH":
        #In the case of a split involving a HASH sharding scheme,
        #the shard that is split gets a new shard_id, while the split
        #gets the new computed lower_bound and also a new shard id.
        #NOTE: How the shard that is split retains its lower_bound.
        HashShardingSpecification.add_hash_split(
            range_sharding_spec.shard_mapping_id, new_shard_1.shard_id,
            range_sharding_spec.lower_bound)
        HashShardingSpecification.add_hash_split(
            range_sharding_spec.shard_mapping_id, new_shard_2.shard_id,
            split_value)
    else:
        #Add the new ranges. Note that the shard being split retains
        #its lower_bound, while the new shard gets the computed,
        #lower_bound.
        RangeShardingSpecification.add(range_sharding_spec.shard_mapping_id,
                                       range_sharding_spec.lower_bound,
                                       new_shard_1.shard_id)
        RangeShardingSpecification.add(range_sharding_spec.shard_mapping_id,
                                       split_value, new_shard_2.shard_id)

    #The sleep ensures that the connector have refreshed their caches with the
    #new shards that have been added as a result of the split.
    time.sleep(_utils.TTL)

    #The source shard group master would have been marked as read only
    #during the sync. Remove the read_only flag.
    source_group = Group.fetch(source_group_id)
    if source_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (source_group_id, ))

    source_group_master = MySQLServer.fetch(source_group.master)
    if source_group_master is None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_GROUP_MASTER_NOT_FOUND)
    source_group_master.connect()

    #Kill all the existing connections on the servers
    source_group.kill_connections_on_servers()

    #Allow connections on the source group master
    source_group_master.read_only = False

    #Allow connections on the destination group master
    destn_group_master.read_only = False

    #Setup replication for the new group from the global server
    _group_replication.setup_group_replication \
            (shard_mapping_defn[2], destination_group_id)

    #Enable the split shards
    new_shard_1.enable()
    new_shard_2.enable()

    #Trigger changing the mappings for the shard that was copied
    if not update_only:
        _events.trigger_within_procedure(PRUNE_SHARDS, new_shard_1.shard_id,
                                         new_shard_2.shard_id, prune_limit)
Ejemplo n.º 14
0
def _check_shard_information(shard_id, destn_group_id, mysqldump_binary,
                             mysqlclient_binary, split_value, config_file,
                             prune_limit, cmd, update_only):
    """Verify the sharding information before starting a re-sharding operation.

    :param shard_id: The destination shard ID.
    :param destn_group_id: The Destination group ID.
    :param mysqldump_binary: The path to the mysqldump binary.
    :param mysqlclient_binary: The path to the mysqlclient binary.
    :param split_value: The point at which the sharding definition
                        should be split.
    :param config_file: The complete path to the fabric configuration
                        file.
    :param prune_limit: The number of DELETEs that should be
                        done in one batch.
    :param cmd: Indicates if it is a split or a move being executed.
    :param update_only: If the operation is a update only operation.
    """
    if not _services_utils.is_valid_binary(mysqldump_binary):
        raise _errors.ShardingError(_services_sharding.MYSQLDUMP_NOT_FOUND %
                                    mysqldump_binary)

    if not _services_utils.is_valid_binary(mysqlclient_binary):
        raise _errors.ShardingError(_services_sharding.MYSQLCLIENT_NOT_FOUND %
                                    mysqlclient_binary)

    if cmd == "SPLIT":
        range_sharding_spec, _, shard_mappings, _ = \
            _services_sharding.verify_and_fetch_shard(shard_id)
        upper_bound = \
            SHARDING_SPECIFICATION_HANDLER[shard_mappings[0].type_name].\
                        get_upper_bound(
                            range_sharding_spec.lower_bound,
                            range_sharding_spec.shard_mapping_id,
                            shard_mappings[0].type_name
                          )
        #If the underlying sharding scheme is a HASH. When a shard is split,
        #all the tables that are part of the shard, have the same sharding
        #scheme. All the shard mappings associated with this shard_id will be
        #of the same sharding type. Hence it is safe to use one of the shard
        #mappings.
        if shard_mappings[0].type_name == "HASH":
            if split_value is not None:
                raise _errors.ShardingError(
                    _services_sharding.NO_LOWER_BOUND_FOR_HASH_SHARDING)
            if upper_bound is None:
                #While splitting a range, retrieve the next upper bound and
                #find the mid-point, in the case where the next upper_bound
                #is unavailable pick the maximum value in the set of values in
                #the shard.
                upper_bound = HashShardingSpecification.fetch_max_key(shard_id)

            #Calculate the split value.
            split_value = \
                SHARDING_DATATYPE_HANDLER[shard_mappings[0].type_name].\
                split_value(
                    range_sharding_spec.lower_bound,
                    upper_bound
                )
        elif split_value is not None:
            if not (SHARDING_DATATYPE_HANDLER[shard_mappings[0].type_name].\
                    is_valid_split_value(
                        split_value, range_sharding_spec.lower_bound,
                        upper_bound
                    )
                ):
                raise _errors.ShardingError(
                    _services_sharding.INVALID_LOWER_BOUND_VALUE %
                    (split_value, ))
        elif split_value is None:
            raise _errors.ShardingError(
                _services_sharding.SPLIT_VALUE_NOT_DEFINED)

    #Ensure that the group does not already contain a shard.
    if Shards.lookup_shard_id(destn_group_id) is not None:
        raise _errors.ShardingError(
            _services_sharding.SHARD_MOVE_DESTINATION_NOT_EMPTY %
            (destn_group_id, ))

    #Fetch the group information for the source shard that
    #needs to be moved.
    source_shard = Shards.fetch(shard_id)
    if source_shard is None:
        raise _errors.ShardingError(_services_sharding.SHARD_NOT_FOUND %
                                    (shard_id, ))

    #Fetch the group_id and the group that hosts the source shard.
    source_group_id = source_shard.group_id

    destn_group = Group.fetch(destn_group_id)
    if destn_group is None:
        raise _errors.ShardingError(_services_sharding.SHARD_GROUP_NOT_FOUND %
                                    (destn_group_id, ))

    if not update_only:
        _events.trigger_within_procedure(BACKUP_SOURCE_SHARD, shard_id,
                                         source_group_id, destn_group_id,
                                         mysqldump_binary, mysqlclient_binary,
                                         split_value, config_file, prune_limit,
                                         cmd, update_only)
    else:
        _events.trigger_within_procedure(SETUP_RESHARDING_SWITCH, shard_id,
                                         source_group_id, destn_group_id,
                                         split_value, prune_limit, cmd,
                                         update_only)
Ejemplo n.º 15
0
def _add_shard(shard_mapping_id, groupid_lb_list, state):
    """Add the RANGE shard specification. This represents a single instance
    of a shard specification that maps a key RANGE to a server.

    :param shard_mapping_id: The unique identification for a shard mapping.
    :param groupid_lb_list: The list of group_id, lower_bounds pairs in the
                        format, group_id/lower_bound, group_id/lower_bound... .
    :param state: Indicates whether a given shard is ENABLED or DISABLED

    :return: True if the add succeeded.
                False otherwise.
    :raises: ShardingError If the group on which the shard is being
                           created does not exist,
                           If the shard_mapping_id is not found,
                           If adding the shard definition fails,
                           If the state of the shard is an invalid
                           value,
                           If the range definition is invalid.
    """
    shard_mapping = ShardMapping.fetch_shard_mapping_defn(shard_mapping_id)
    if shard_mapping is None:
        raise _errors.ShardingError(SHARD_MAPPING_NOT_FOUND % \
                                                    (shard_mapping_id,  ))

    schema_type = shard_mapping[1]

    if len(RangeShardingSpecification.list(shard_mapping_id)) != 0:
        raise _errors.ShardingError(SHARDS_ALREADY_EXIST)

    group_id_list, lower_bound_list = \
        _utils.get_group_lower_bound_list(groupid_lb_list)

    if (len(group_id_list) != len(lower_bound_list)) and\
        schema_type == "RANGE":
        raise _errors.ShardingError(LOWER_BOUND_GROUP_ID_COUNT_MISMATCH)

    if len(lower_bound_list) != 0 and schema_type == "HASH":
        raise _errors.ShardingError(LOWER_BOUND_AUTO_GENERATED)

    if schema_type in Shards.VALID_RANGE_SHARDING_TYPES:
        for lower_bound in lower_bound_list:
            if not SHARDING_DATATYPE_HANDLER[schema_type].\
                        is_valid_lower_bound(lower_bound):
                raise _errors.ShardingError(INVALID_LOWER_BOUND_VALUE %
                                            (lower_bound, ))

    state = state.upper()
    if state not in Shards.VALID_SHARD_STATES:
        raise _errors.ShardingError(INVALID_SHARD_STATE % (state, ))

    for index, group_id in enumerate(group_id_list):
        shard = Shards.add(group_id, state)

        shard_id = shard.shard_id

        if schema_type == "HASH":
            HashShardingSpecification.add(shard_mapping_id, shard_id)
            _LOGGER.debug("Added Shard (map id = %s, id = %s).",
                          shard_mapping_id, shard_id)
        else:
            range_sharding_specification = \
                SHARDING_SPECIFICATION_HANDLER[schema_type].add(
                                                shard_mapping_id,
                                                lower_bound_list[index],
                                                shard_id
                                            )
            _LOGGER.debug(
                "Added Shard (map id = %s, lower bound = %s, id = %s).",
                range_sharding_specification.shard_mapping_id,
                range_sharding_specification.lower_bound,
                range_sharding_specification.shard_id)

        #If the shard is added in a DISABLED state  do not setup replication
        #with the primary of the global group. Basically setup replication only
        #if the shard is ENABLED.
        if state == "ENABLED":
            _setup_shard_group_replication(shard_id)