예제 #1
0
    def Register(self, request, context):
        """
        register new clients on the fly.
        Each client must get registered before getting the global model.
        The server will expect updates from the registered clients
        for multiple federated rounds.

        This function does not change min_num_clients and max_num_clients.
        """
        token = self.login_client(request, context)
        client_ip = context.peer().split(':')[1]
        # allow model requests/updates from this client

        if self.is_from_authorized_client(token):
            # previously known client, potentially contributed already
            # will join the next round
            return fed_msg.FederatedSummary(comment='Already registered',
                                            token=token)

        if len(self.auth_client_id) >= self.max_num_clients:
            context.abort(grpc.StatusCode.RESOURCE_EXHAUSTED,
                          'Maximum number of clients reached')

        # new client will join the current round immediately
        self.auth_client_id.update({token: time.time()})
        self.tokens[token] = self.model_meta_info
        self.logger.info(
            'Client: New client {} joined. Sent token: {}.  Total clients: {}'.
            format(request.client_id + '@' + client_ip, token,
                   len(self.auth_client_id)))
        return fed_msg.FederatedSummary(comment='New client registered',
                                        token=token)
예제 #2
0
    def SubmitUpdate(self, request, context):
        """
        handling client's submission of the federated updates
        running aggregation if there are enough updates
        """
        self.update_error = False
        token = self.validate_client(request.client, context)

        if token is None:
            response_comment = 'Ignored the submit from invalid client. '
            self.logger.info(response_comment)

        # if len(self.accumulator) > self.min_num_clients:
        #     context.abort(grpc.StatusCode.ALREADY_EXISTS,
        #                   'Contrib: already enough in the current round')
        else:

            model_meta = self.is_valid_contribution(request.client.meta)
            if model_meta is None:
                context.abort(grpc.StatusCode.FAILED_PRECONDITION,
                              'Contrib: invalid for the current round')
                response_comment = 'Invalid contribution. '
                self.logger.info(response_comment)
            else:

                client_contrib_id = '{}_{}_{}'.format(model_meta.task.name, token,
                                                      model_meta.current_round)

                start_time = request.client.meta.created
                timenow = Timestamp()
                timenow.GetCurrentTime()
                time_seconds = timenow.seconds - start_time.seconds
                self.logger.info(
                    'received %s (%s Bytes, %s seconds)', client_contrib_id,
                    request.ByteSize(), time_seconds or 'less than 1')

                if self.save_contribution(client_contrib_id, request):
                    with self.lock:
                        self.accumulator.append(request)
                        # if self.get_enough_updates():
                        #     self.aggregate()
                        num_of_updates = len(self.accumulator)

                    # Only the first one meets the minimum clients trigger the aggregation.
                    if num_of_updates == self.min_num_clients:
                        if num_of_updates < len(self.auth_client_id):
                            self.logger.debug("Starting to wait. {}".format(self.wait_after_min_clients))
                            time.sleep(self.wait_after_min_clients)
                        self.aggregate()

                response_comment = \
                    'Received round {} from {} ({} Bytes, {} seconds)'.format(
                        request.client.meta.current_round, request.client.uid,
                        request.ByteSize(), time_seconds or 'less than 1')

        summary_info = fed_msg.FederatedSummary(comment=response_comment)
        if self.model_meta_info is not None:
            summary_info.meta.CopyFrom(self.model_meta_info)
        return summary_info
예제 #3
0
    def Quit(self, request, context):
        """
        existing client quits the federated training process.
        Server will stop sharing the global model with the client,
        further contribution will be rejected.

        This function does not change min_num_clients and max_num_clients.
        """
        token = self.validate_client(request, context)
        self.auth_client_id.pop(token)
        self.tokens.pop(token, None)
        self.logger.info('Client: {} left.  Total clients: {}'.format(
            token, len(self.auth_client_id)))
        return fed_msg.FederatedSummary(comment='Removed client')
예제 #4
0
 def Heartbeat(self, request, context):
     token = request.token
     self.auth_client_id.update({token: time.time()})
     self.logger.debug('Receive heartbeat from Client:{}'.format(token))
     summary_info = fed_msg.FederatedSummary()
     return summary_info