2017-04-19 21:44:31 +03:00
/* -------------------------------------------------------------------------- */
2023-01-09 14:23:19 +03:00
/* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems */
2017-04-19 21:44:31 +03:00
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
/* not use this file except in compliance with the License. You may obtain */
/* a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/* See the License for the specific language governing permissions and */
/* limitations under the License. */
/* -------------------------------------------------------------------------- */
2017-04-27 02:03:44 +03:00
# ifndef REPLICA_REQUEST_H_
# define REPLICA_REQUEST_H_
2017-04-19 21:44:31 +03:00
2017-04-20 17:13:41 +03:00
# include "SyncRequest.h"
2017-04-19 21:44:31 +03:00
/**
* This class represents a log entry replication request . The replication request
* is synchronous : once it has been replicated in a majority of followers the
* client is notified ( SqlDB : : exec_wr ( ) call ) and DB updated .
*/
2017-04-27 02:03:44 +03:00
class ReplicaRequest : public SyncRequest
2017-04-19 21:44:31 +03:00
{
public :
2019-04-08 18:43:12 +03:00
ReplicaRequest ( uint64_t i ) : _index ( i ) , _to_commit ( - 1 ) , _replicas ( 1 ) { } ;
2017-04-19 21:44:31 +03:00
2017-04-27 02:03:44 +03:00
~ ReplicaRequest ( ) { } ;
2017-04-19 21:44:31 +03:00
/**
2017-04-27 02:03:44 +03:00
* This function updates the number of replicas of the record and decrement
* the number of servers left to reach majority consensus . If it reaches 0 ,
* the client is notified
* @ return number of replicas for this log
2017-04-19 21:44:31 +03:00
*/
2018-07-30 14:14:14 +03:00
int add_replica ( )
2017-04-20 17:13:41 +03:00
{
2017-04-27 02:03:44 +03:00
int __replicas ;
2017-04-19 21:44:31 +03:00
2017-04-27 02:03:44 +03:00
_replicas + + ;
2017-04-19 21:44:31 +03:00
2017-04-27 02:03:44 +03:00
if ( _to_commit > 0 )
{
_to_commit - - ;
}
__replicas = _replicas ;
if ( _to_commit = = 0 )
{
result = true ;
timeout = false ;
notify ( ) ;
}
2017-04-21 23:32:30 +03:00
2017-04-27 02:03:44 +03:00
return __replicas ;
}
/* ---------------------------------------------------------------------- */
/* Class access methods */
/* ---------------------------------------------------------------------- */
2019-04-08 18:43:12 +03:00
uint64_t index ( )
2017-04-20 17:13:41 +03:00
{
2017-04-27 02:03:44 +03:00
return _index ;
}
2017-04-19 21:44:31 +03:00
2017-04-25 12:49:52 +03:00
int replicas ( )
{
return _replicas ;
}
int to_commit ( )
{
return _to_commit ;
}
void to_commit ( int c )
{
_to_commit = c ;
}
2017-04-19 21:44:31 +03:00
private :
/**
* Index for this log entry
*/
2019-04-08 18:43:12 +03:00
uint64_t _index ;
2017-04-19 21:44:31 +03:00
/**
* Remaining number of servers that need to replicate this record to commit
* it . Initialized to ( Number_Servers - 1 ) / 2
*/
2017-04-25 12:49:52 +03:00
int _to_commit ;
2017-04-19 21:44:31 +03:00
/**
* Total number of replicas for this entry
*/
2017-04-25 12:49:52 +03:00
int _replicas ;
2017-04-19 21:44:31 +03:00
} ;
2018-07-30 14:14:14 +03:00
/**
* This class represents a map of replication requests . It syncs access between
2020-09-10 14:32:52 +03:00
* RaftManager and DB writer threads . A DB writer allocates and set the
2018-07-30 14:14:14 +03:00
* request and then it waits on it for completion .
*/
class ReplicaRequestMap
{
public :
2020-09-10 14:32:52 +03:00
ReplicaRequestMap ( ) = default ;
2018-07-30 14:14:14 +03:00
2020-09-10 14:32:52 +03:00
virtual ~ ReplicaRequestMap ( ) = default ;
2018-07-30 14:14:14 +03:00
/**
* Increments the number of replicas of this request . If it can be
* committed the request is removed from the map
* @ param rindex of the request
*
* @ return the number of replicas to commit , if 0 it can be committed
*/
2019-04-08 18:43:12 +03:00
int add_replica ( uint64_t rindex )
2018-07-30 14:14:14 +03:00
{
int to_commit = - 1 ;
2020-09-10 14:32:52 +03:00
std : : lock_guard < std : : mutex > lock ( _mutex ) ;
2018-07-30 14:14:14 +03:00
2020-09-17 12:10:55 +03:00
auto it = requests . find ( rindex ) ;
2018-07-30 14:14:14 +03:00
2020-09-17 12:10:55 +03:00
if ( it ! = requests . end ( ) & & it - > second )
2018-07-30 14:14:14 +03:00
{
it - > second - > add_replica ( ) ;
to_commit = it - > second - > to_commit ( ) ;
if ( to_commit = = 0 )
{
requests . erase ( it ) ;
}
}
return to_commit ;
}
2018-09-06 18:22:52 +03:00
/**
* Allocated an empty replica request . It marks a writer thread will wait
* on this request .
* @ param rindex of the request
*/
2019-04-08 18:43:12 +03:00
void allocate ( uint64_t rindex )
2018-09-06 18:22:52 +03:00
{
2020-09-10 14:32:52 +03:00
std : : lock_guard < std : : mutex > lock ( _mutex ) ;
2018-09-06 18:22:52 +03:00
requests . insert ( std : : make_pair ( rindex , ( ReplicaRequest * ) 0 ) ) ;
}
2018-07-30 14:14:14 +03:00
/**
* Set the replication request associated to this index . If there is no
* previous request associated to the index it is created .
* @ param rindex of the request
* @ param rr replica request pointer
*/
2019-04-08 18:43:12 +03:00
void set ( uint64_t rindex , ReplicaRequest * rr )
2018-07-30 14:14:14 +03:00
{
2020-09-10 14:32:52 +03:00
std : : lock_guard < std : : mutex > lock ( _mutex ) ;
2018-09-06 18:22:52 +03:00
2020-09-10 14:32:52 +03:00
requests [ rindex ] = rr ;
2018-09-06 18:22:52 +03:00
}
/**
* Remove a replication request associated to this index
* @ param rindex of the request
*/
2019-04-08 18:43:12 +03:00
void remove ( uint64_t rindex )
2018-09-06 18:22:52 +03:00
{
2020-09-10 14:32:52 +03:00
std : : lock_guard < std : : mutex > lock ( _mutex ) ;
2018-08-24 18:56:14 +03:00
2020-09-10 14:32:52 +03:00
requests . erase ( rindex ) ;
2018-08-24 18:56:14 +03:00
}
2018-07-30 14:14:14 +03:00
/**
* Notify all writers and clear the replica map
*/
void clear ( )
{
2020-09-10 14:32:52 +03:00
std : : lock_guard < std : : mutex > lock ( _mutex ) ;
2018-07-30 14:14:14 +03:00
2020-09-10 14:32:52 +03:00
for ( auto it = requests . begin ( ) ; it ! = requests . end ( ) ; + + it )
2018-07-30 14:14:14 +03:00
{
if ( it - > second = = 0 )
{
continue ;
}
it - > second - > result = false ;
it - > second - > timeout = false ;
it - > second - > message = " oned is now follower " ;
it - > second - > notify ( ) ;
}
requests . clear ( ) ;
}
2018-09-06 18:22:52 +03:00
/**
* @ return true if a replica request is set for this index
*/
2019-04-08 18:43:12 +03:00
bool is_replicable ( uint64_t rindex )
2018-09-06 18:22:52 +03:00
{
2020-09-10 14:32:52 +03:00
std : : lock_guard < std : : mutex > lock ( _mutex ) ;
2018-09-06 18:22:52 +03:00
2020-09-10 14:32:52 +03:00
auto it = requests . find ( rindex ) ;
2018-09-06 18:22:52 +03:00
2020-09-10 14:32:52 +03:00
return ( it = = requests . end ( ) ) | | ( it - > second ! = nullptr ) ;
2018-09-06 18:22:52 +03:00
}
2018-07-30 14:14:14 +03:00
private :
2018-08-30 03:26:30 +03:00
2020-09-10 14:32:52 +03:00
std : : mutex _mutex ;
2018-07-30 14:14:14 +03:00
/**
* Clients waiting for a log replication
*/
2019-04-08 18:43:12 +03:00
std : : map < uint64_t , ReplicaRequest * > requests ;
2018-07-30 14:14:14 +03:00
} ;
2017-04-27 02:03:44 +03:00
# endif /*REPLICA_REQUEST_H_*/
2017-04-19 21:44:31 +03:00