2018-11-27 16:11:46 +00:00
# include "ExternalLoader.h"
2019-06-02 12:11:01 +00:00
# include <mutex>
# include <pcg_random.hpp>
# include <Common/Config/AbstractConfigurationComparison.h>
2017-10-06 11:10:01 +00:00
# include <Common/Exception.h>
2019-06-02 12:11:01 +00:00
# include <Common/StringUtils/StringUtils.h>
# include <Common/ThreadPool.h>
# include <Common/randomSeed.h>
2017-10-06 11:10:01 +00:00
# include <Common/setThreadName.h>
2019-11-09 19:14:51 +00:00
# include <ext/chrono_io.h>
2017-10-06 10:31:06 +00:00
# include <ext/scope_guard.h>
2019-12-12 16:41:41 +00:00
# include <boost/range/adaptor/map.hpp>
# include <boost/range/algorithm/copy.hpp>
2019-12-12 18:33:43 +00:00
# include <unordered_set>
2019-06-02 12:11:01 +00:00
2017-10-06 10:31:06 +00:00
namespace DB
{
namespace ErrorCodes
{
2019-06-02 12:11:01 +00:00
extern const int LOGICAL_ERROR ;
extern const int BAD_ARGUMENTS ;
2017-10-06 10:31:06 +00:00
}
2019-12-12 18:33:43 +00:00
2019-10-22 12:57:58 +00:00
namespace
{
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType convertTo ( ExternalLoader : : LoadResult result )
{
if constexpr ( std : : is_same_v < ReturnType , ExternalLoader : : LoadResult > )
return result ;
else
{
static_assert ( std : : is_same_v < ReturnType , ExternalLoader : : LoadablePtr > ) ;
return std : : move ( result . object ) ;
}
}
2019-10-22 12:57:58 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType convertTo ( ExternalLoader : : LoadResults results )
2019-10-22 12:57:58 +00:00
{
2019-12-12 18:33:43 +00:00
if constexpr ( std : : is_same_v < ReturnType , ExternalLoader : : LoadResults > )
return results ;
else
{
static_assert ( std : : is_same_v < ReturnType , ExternalLoader : : Loadables > ) ;
ExternalLoader : : Loadables objects ;
objects . reserve ( results . size ( ) ) ;
for ( const auto & result : results )
{
if ( auto object = std : : move ( result . object ) )
objects . push_back ( std : : move ( object ) ) ;
}
return objects ;
}
2019-10-22 12:57:58 +00:00
}
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType notExists ( const String & name )
2019-10-22 12:57:58 +00:00
{
2019-12-12 18:33:43 +00:00
if constexpr ( std : : is_same_v < ReturnType , ExternalLoader : : LoadResult > )
{
ExternalLoader : : LoadResult res ;
res . name = name ;
return res ;
}
else
{
static_assert ( std : : is_same_v < ReturnType , ExternalLoader : : LoadablePtr > ) ;
return nullptr ;
}
2019-10-22 12:57:58 +00:00
}
2019-12-12 18:33:43 +00:00
/// Lock mutex only in async mode
/// In other case does nothing
struct LoadingGuardForAsyncLoad
{
std : : unique_lock < std : : mutex > lock ;
LoadingGuardForAsyncLoad ( bool async , std : : mutex & mutex )
{
if ( async )
lock = std : : unique_lock ( mutex ) ;
}
} ;
2019-10-22 12:57:58 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
struct ExternalLoader : : ObjectConfig
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
Poco : : AutoPtr < Poco : : Util : : AbstractConfiguration > config ;
String key_in_config ;
2019-10-17 13:05:12 +00:00
String repository_name ;
2019-12-30 23:30:06 +00:00
bool from_temp_repository = false ;
2019-12-12 16:41:41 +00:00
String path ;
2019-06-02 12:11:01 +00:00
} ;
2017-10-06 10:31:06 +00:00
2019-09-30 16:12:08 +00:00
/** Reads configurations from configuration repository and parses it.
2019-06-02 12:11:01 +00:00
*/
2019-09-30 16:12:08 +00:00
class ExternalLoader : : LoadablesConfigReader : private boost : : noncopyable
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
public :
2019-09-30 16:12:08 +00:00
LoadablesConfigReader ( const String & type_name_ , Logger * log_ )
: type_name ( type_name_ ) , log ( log_ )
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
}
2019-09-30 16:12:08 +00:00
~ LoadablesConfigReader ( ) = default ;
2017-10-06 10:31:06 +00:00
2019-12-30 23:30:06 +00:00
using Repository = IExternalLoaderConfigRepository ;
2019-12-12 16:41:41 +00:00
2019-12-30 23:30:06 +00:00
void addConfigRepository ( std : : unique_ptr < Repository > repository )
2019-06-02 12:11:01 +00:00
{
std : : lock_guard lock { mutex } ;
2019-12-30 23:30:06 +00:00
auto * ptr = repository . get ( ) ;
repositories . emplace ( ptr , RepositoryInfo { std : : move ( repository ) , { } } ) ;
2019-12-12 16:41:41 +00:00
need_collect_object_configs = true ;
2017-10-06 10:31:06 +00:00
}
2019-12-30 23:30:06 +00:00
void removeConfigRepository ( Repository * repository )
2019-10-15 18:04:17 +00:00
{
std : : lock_guard lock { mutex } ;
2019-12-30 23:30:06 +00:00
auto it = repositories . find ( repository ) ;
2019-12-12 16:41:41 +00:00
if ( it = = repositories . end ( ) )
2019-12-30 23:30:06 +00:00
return ;
2019-12-12 16:41:41 +00:00
repositories . erase ( it ) ;
need_collect_object_configs = true ;
2019-12-30 23:30:06 +00:00
}
void setConfigSettings ( const ExternalLoaderConfigSettings & settings_ )
{
std : : lock_guard lock { mutex } ;
settings = settings_ ;
2019-10-15 18:04:17 +00:00
}
2019-09-30 11:18:01 +00:00
using ObjectConfigsPtr = std : : shared_ptr < const std : : unordered_map < String /* object's name */ , ObjectConfig > > ;
2017-10-06 10:31:06 +00:00
2019-12-12 16:41:41 +00:00
/// Reads all repositories.
2019-09-30 11:18:01 +00:00
ObjectConfigsPtr read ( )
2019-06-02 12:11:01 +00:00
{
2019-10-18 15:44:32 +00:00
std : : lock_guard lock ( mutex ) ;
2019-12-12 16:41:41 +00:00
readRepositories ( ) ;
collectObjectConfigs ( ) ;
return object_configs ;
2019-10-18 15:44:32 +00:00
}
2019-12-12 16:41:41 +00:00
/// Reads only a specified repository.
/// This functions checks only a specified repository but returns configs from all repositories.
ObjectConfigsPtr read ( const String & repository_name )
2019-10-18 15:44:32 +00:00
{
std : : lock_guard lock ( mutex ) ;
2019-12-12 16:41:41 +00:00
readRepositories ( repository_name ) ;
collectObjectConfigs ( ) ;
return object_configs ;
}
2019-10-18 15:44:32 +00:00
2019-12-12 16:41:41 +00:00
/// Reads only a specified path from a specified repository.
/// This functions checks only a specified repository but returns configs from all repositories.
ObjectConfigsPtr read ( const String & repository_name , const String & path )
{
std : : lock_guard lock ( mutex ) ;
readRepositories ( repository_name , path ) ;
collectObjectConfigs ( ) ;
return object_configs ;
2019-10-18 15:44:32 +00:00
}
private :
2019-12-12 16:41:41 +00:00
struct FileInfo
2019-10-18 15:44:32 +00:00
{
Poco : : Timestamp last_update_time = 0 ;
2019-12-12 16:41:41 +00:00
std : : vector < std : : pair < String , ObjectConfig > > objects ; // Parsed contents of the file.
bool in_use = true ; // Whether the `FileInfo` should be destroyed because the correspondent file is deleted.
2019-10-18 15:44:32 +00:00
} ;
2019-12-12 16:41:41 +00:00
struct RepositoryInfo
2019-10-18 15:44:32 +00:00
{
2019-12-30 23:30:06 +00:00
std : : unique_ptr < Repository > repository ;
2019-12-12 16:41:41 +00:00
std : : unordered_map < String /* path */ , FileInfo > files ;
} ;
2019-04-10 18:36:52 +00:00
2019-12-12 16:41:41 +00:00
/// Reads the repositories.
/// Checks last modification times of files and read those files which are new or changed.
void readRepositories ( const std : : optional < String > & only_repository_name = { } , const std : : optional < String > & only_path = { } )
2017-10-06 10:31:06 +00:00
{
2019-12-30 23:30:06 +00:00
for ( auto & [ repository , repository_info ] : repositories )
2019-06-02 12:11:01 +00:00
{
2019-12-30 23:30:06 +00:00
if ( only_repository_name & & ( repository - > getName ( ) ! = * only_repository_name ) )
continue ;
2019-12-12 16:41:41 +00:00
for ( auto & file_info : repository_info . files | boost : : adaptors : : map_values )
file_info . in_use = false ;
Strings existing_paths ;
if ( only_path )
{
2019-12-30 23:30:06 +00:00
if ( repository - > exists ( * only_path ) )
2019-12-12 16:41:41 +00:00
existing_paths . push_back ( * only_path ) ;
}
else
2019-12-30 23:30:06 +00:00
boost : : copy ( repository - > getAllLoadablesDefinitionNames ( ) , std : : back_inserter ( existing_paths ) ) ;
2019-12-12 16:41:41 +00:00
for ( const auto & path : existing_paths )
2019-06-02 12:11:01 +00:00
{
2019-12-12 16:41:41 +00:00
auto it = repository_info . files . find ( path ) ;
if ( it ! = repository_info . files . end ( ) )
2019-06-02 12:11:01 +00:00
{
2019-12-12 16:41:41 +00:00
FileInfo & file_info = it - > second ;
2019-12-30 23:30:06 +00:00
if ( readFileInfo ( file_info , * repository , path ) )
2019-12-12 16:41:41 +00:00
need_collect_object_configs = true ;
2019-06-02 12:11:01 +00:00
}
else
{
2019-12-12 16:41:41 +00:00
FileInfo file_info ;
2019-12-30 23:30:06 +00:00
if ( readFileInfo ( file_info , * repository , path ) )
2019-06-02 12:11:01 +00:00
{
2019-12-12 16:41:41 +00:00
repository_info . files . emplace ( path , std : : move ( file_info ) ) ;
need_collect_object_configs = true ;
2019-06-02 12:11:01 +00:00
}
}
}
2017-10-06 10:31:06 +00:00
2019-12-12 16:41:41 +00:00
Strings deleted_paths ;
for ( auto & [ path , file_info ] : repository_info . files )
{
if ( file_info . in_use )
continue ;
if ( only_path & & ( * only_path ! = path ) )
continue ;
deleted_paths . emplace_back ( path ) ;
}
if ( ! deleted_paths . empty ( ) )
{
for ( const String & deleted_path : deleted_paths )
repository_info . files . erase ( deleted_path ) ;
need_collect_object_configs = true ;
}
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
}
2019-12-12 16:41:41 +00:00
/// Reads a file, returns true if the file is new or changed.
bool readFileInfo (
FileInfo & file_info ,
2019-09-30 16:12:08 +00:00
IExternalLoaderConfigRepository & repository ,
2019-12-30 23:30:06 +00:00
const String & path ) const
2019-06-02 12:11:01 +00:00
{
try
{
2019-12-12 16:41:41 +00:00
if ( path . empty ( ) | | ! repository . exists ( path ) )
2019-06-02 12:11:01 +00:00
{
2019-12-12 16:41:41 +00:00
LOG_WARNING ( log , " Config file ' " + path + " ' does not exist " ) ;
2019-06-02 12:11:01 +00:00
return false ;
}
2017-10-06 10:31:06 +00:00
2019-12-12 16:41:41 +00:00
auto update_time_from_repository = repository . getUpdateTime ( path ) ;
2019-10-01 08:58:47 +00:00
/// Actually it can't be less, but for sure we check less or equal
2019-12-12 16:41:41 +00:00
if ( update_time_from_repository < = file_info . last_update_time )
2019-06-02 12:11:01 +00:00
{
2019-12-12 16:41:41 +00:00
file_info . in_use = true ;
2019-06-02 12:11:01 +00:00
return false ;
}
2017-10-06 10:31:06 +00:00
2019-12-12 16:41:41 +00:00
auto file_contents = repository . load ( path ) ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// get all objects' definitions
Poco : : Util : : AbstractConfiguration : : Keys keys ;
file_contents - > keys ( keys ) ;
2017-10-06 10:31:06 +00:00
2019-10-17 17:18:54 +00:00
/// for each object defined in repositories
2019-12-12 16:41:41 +00:00
std : : vector < std : : pair < String , ObjectConfig > > object_configs_from_file ;
2019-06-02 12:11:01 +00:00
for ( const auto & key : keys )
{
if ( ! startsWith ( key , settings . external_config ) )
{
if ( ! startsWith ( key , " comment " ) & & ! startsWith ( key , " include_from " ) )
2019-12-12 16:41:41 +00:00
LOG_WARNING ( log , path < < " : file contains unknown node ' " < < key < < " ', expected ' " < < settings . external_config < < " ' " ) ;
2019-06-02 12:11:01 +00:00
continue ;
}
2019-12-12 16:41:41 +00:00
String object_name = file_contents - > getString ( key + " . " + settings . external_name ) ;
if ( object_name . empty ( ) )
2019-06-02 12:11:01 +00:00
{
2019-12-12 16:41:41 +00:00
LOG_WARNING ( log , path < < " : node ' " < < key < < " ' defines " < < type_name < < " with an empty name. It's not allowed " ) ;
2019-06-02 12:11:01 +00:00
continue ;
}
2019-12-25 23:12:12 +00:00
String database ;
if ( ! settings . external_database . empty ( ) )
database = file_contents - > getString ( key + " . " + settings . external_database , " " ) ;
if ( ! database . empty ( ) )
object_name = database + " . " + object_name ;
2019-12-30 23:30:06 +00:00
object_configs_from_file . emplace_back ( object_name , ObjectConfig { file_contents , key , { } , { } , { } } ) ;
2019-06-02 12:11:01 +00:00
}
2019-12-12 16:41:41 +00:00
file_info . objects = std : : move ( object_configs_from_file ) ;
file_info . last_update_time = update_time_from_repository ;
file_info . in_use = true ;
2019-06-02 12:11:01 +00:00
return true ;
}
catch ( . . . )
{
2019-12-12 16:41:41 +00:00
tryLogCurrentException ( log , " Failed to load config file ' " + path + " ' " ) ;
2019-06-02 12:11:01 +00:00
return false ;
}
}
2019-12-12 16:41:41 +00:00
/// Builds a map of current configurations of objects.
void collectObjectConfigs ( )
{
if ( ! need_collect_object_configs )
return ;
need_collect_object_configs = false ;
// Generate new result.
auto new_configs = std : : make_shared < std : : unordered_map < String /* object's name */ , ObjectConfig > > ( ) ;
2019-12-30 23:30:06 +00:00
for ( const auto & [ repository , repository_info ] : repositories )
2019-12-12 16:41:41 +00:00
{
for ( const auto & [ path , file_info ] : repository_info . files )
{
for ( const auto & [ object_name , object_config ] : file_info . objects )
{
auto already_added_it = new_configs - > find ( object_name ) ;
if ( already_added_it = = new_configs - > end ( ) )
{
auto & new_config = new_configs - > emplace ( object_name , object_config ) . first - > second ;
2019-12-30 23:30:06 +00:00
new_config . from_temp_repository = repository - > isTemporary ( ) ;
new_config . repository_name = repository - > getName ( ) ;
2019-12-12 16:41:41 +00:00
new_config . path = path ;
}
else
{
const auto & already_added = already_added_it - > second ;
2019-12-30 23:30:06 +00:00
if ( ! already_added . from_temp_repository & & ! repository - > isTemporary ( ) )
2019-12-12 16:41:41 +00:00
{
LOG_WARNING (
log ,
type_name < < " ' " < < object_name < < " ' is found "
2019-12-30 23:30:06 +00:00
< < ( ( ( path = = already_added . path ) & & ( repository - > getName ( ) = = already_added . repository_name ) )
2019-12-12 16:41:41 +00:00
? ( " twice in the same file ' " + path + " ' " )
: ( " both in file ' " + already_added . path + " ' and ' " + path + " ' " ) ) ) ;
}
}
}
}
}
object_configs = new_configs ;
}
2019-10-18 15:44:32 +00:00
2019-06-02 12:11:01 +00:00
const String type_name ;
Logger * log ;
2019-04-10 18:36:52 +00:00
2019-06-02 12:11:01 +00:00
std : : mutex mutex ;
2019-12-30 23:30:06 +00:00
ExternalLoaderConfigSettings settings ;
std : : unordered_map < Repository * , RepositoryInfo > repositories ;
2019-12-12 16:41:41 +00:00
ObjectConfigsPtr object_configs ;
bool need_collect_object_configs = false ;
2019-06-02 12:11:01 +00:00
} ;
2017-10-06 10:31:06 +00:00
2019-09-30 16:12:08 +00:00
/** Manages loading and reloading objects. Uses configurations from the class LoadablesConfigReader.
2019-06-02 12:11:01 +00:00
* Supports parallel loading .
*/
class ExternalLoader : : LoadingDispatcher : private boost : : noncopyable
{
public :
/// Called to load or reload an object.
2019-07-17 08:39:36 +00:00
using CreateObjectFunction = std : : function < LoadablePtr (
2019-12-04 23:53:06 +00:00
const String & /* name */ , const ObjectConfig & /* config */ , const LoadablePtr & /* previous_version */ ) > ;
2019-06-02 12:11:01 +00:00
LoadingDispatcher (
const CreateObjectFunction & create_object_function_ ,
const String & type_name_ ,
Logger * log_ )
: create_object ( create_object_function_ )
, type_name ( type_name_ )
, log ( log_ )
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
~ LoadingDispatcher ( )
{
std : : unique_lock lock { mutex } ;
infos . clear ( ) ; /// We clear this map to tell the threads that we don't want any load results anymore.
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Wait for all the threads to finish.
2019-12-15 13:22:04 +00:00
while ( ! loading_threads . empty ( ) )
2017-10-06 10:31:06 +00:00
{
2019-12-15 13:22:04 +00:00
auto it = loading_threads . begin ( ) ;
2019-06-02 12:11:01 +00:00
auto thread = std : : move ( it - > second ) ;
2019-12-15 13:22:04 +00:00
loading_threads . erase ( it ) ;
2019-06-02 12:11:01 +00:00
lock . unlock ( ) ;
event . notify_all ( ) ;
thread . join ( ) ;
lock . lock ( ) ;
}
}
2019-09-30 16:12:08 +00:00
using ObjectConfigsPtr = LoadablesConfigReader : : ObjectConfigsPtr ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Sets new configurations for all the objects.
2019-09-30 11:18:01 +00:00
void setConfiguration ( const ObjectConfigsPtr & new_configs )
2019-06-02 12:11:01 +00:00
{
std : : lock_guard lock { mutex } ;
if ( configs = = new_configs )
return ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
configs = new_configs ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
std : : vector < String > removed_names ;
for ( auto & [ name , info ] : infos )
{
auto new_config_it = new_configs - > find ( name ) ;
if ( new_config_it = = new_configs - > end ( ) )
removed_names . emplace_back ( name ) ;
2017-10-06 10:31:06 +00:00
else
{
2019-06-02 12:11:01 +00:00
const auto & new_config = new_config_it - > second ;
2019-12-12 16:41:41 +00:00
bool config_is_same = isSameConfiguration ( * info . object_config . config , info . object_config . key_in_config , * new_config . config , new_config . key_in_config ) ;
info . object_config = new_config ;
if ( ! config_is_same )
2019-06-02 12:11:01 +00:00
{
/// Configuration has been changed.
2019-12-15 13:22:04 +00:00
info . object_config = new_config ;
2019-06-02 12:11:01 +00:00
2019-12-12 18:33:43 +00:00
if ( info . triedToLoad ( ) )
2019-06-02 12:11:01 +00:00
{
/// The object has been tried to load before, so it is currently in use or was in use
/// and we should try to reload it with the new config.
2019-12-15 13:22:04 +00:00
startLoading ( info , true ) ;
2019-06-02 12:11:01 +00:00
}
}
}
}
/// Insert to the map those objects which added to the new configuration.
for ( const auto & [ name , config ] : * new_configs )
{
if ( infos . find ( name ) = = infos . end ( ) )
{
2019-12-12 18:33:43 +00:00
Info & info = infos . emplace ( name , Info { name , config } ) . first - > second ;
2019-06-02 12:11:01 +00:00
if ( always_load_everything )
2019-12-15 13:22:04 +00:00
startLoading ( info ) ;
2019-06-02 12:11:01 +00:00
}
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Remove from the map those objects which were removed from the configuration.
for ( const String & name : removed_names )
infos . erase ( name ) ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Maybe we have just added new objects which require to be loaded
/// or maybe we have just removed object which were been loaded,
/// so we should notify `event` to recheck conditions in load() and loadAll() now.
event . notify_all ( ) ;
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Sets whether all the objects from the configuration should be always loaded (even if they aren't used).
void enableAlwaysLoadEverything ( bool enable )
{
std : : lock_guard lock { mutex } ;
if ( always_load_everything = = enable )
return ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
always_load_everything = enable ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
if ( enable )
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
/// Start loading all the objects which were not loaded yet.
for ( auto & [ name , info ] : infos )
2019-12-12 18:33:43 +00:00
if ( ! info . triedToLoad ( ) )
2019-12-15 13:22:04 +00:00
startLoading ( info ) ;
2019-06-02 12:11:01 +00:00
}
}
/// Sets whether the objects should be loaded asynchronously, each loading in a new thread (from the thread pool).
void enableAsyncLoading ( bool enable )
{
enable_async_loading = enable ;
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Returns the status of the object.
/// If the object has not been loaded yet then the function returns Status::NOT_LOADED.
/// If the specified name isn't found in the configuration then the function returns Status::NOT_EXIST.
Status getCurrentStatus ( const String & name ) const
{
std : : lock_guard lock { mutex } ;
const Info * info = getInfo ( name ) ;
if ( ! info )
return Status : : NOT_EXIST ;
return info - > status ( ) ;
}
2018-12-30 01:09:06 +00:00
2019-06-02 12:11:01 +00:00
/// Returns the load result of the object.
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType getCurrentLoadResult ( const String & name ) const
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
std : : lock_guard lock { mutex } ;
const Info * info = getInfo ( name ) ;
if ( ! info )
2019-12-12 18:33:43 +00:00
return notExists < ReturnType > ( name ) ;
return info - > getLoadResult < ReturnType > ( ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Returns all the load results as a map.
/// The function doesn't load anything, it just returns the current load results as is.
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType getCurrentLoadResults ( const FilterByNameFunction & filter ) const
2019-06-02 12:11:01 +00:00
{
std : : lock_guard lock { mutex } ;
2019-12-12 18:33:43 +00:00
return collectLoadResults < ReturnType > ( filter ) ;
2019-06-02 12:11:01 +00:00
}
size_t getNumberOfCurrentlyLoadedObjects ( ) const
{
std : : lock_guard lock { mutex } ;
size_t count = 0 ;
for ( const auto & name_and_info : infos )
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
const auto & info = name_and_info . second ;
if ( info . loaded ( ) )
+ + count ;
}
return count ;
}
2019-03-04 18:19:48 +00:00
2019-06-29 17:27:32 +00:00
bool hasCurrentlyLoadedObjects ( ) const
{
std : : lock_guard lock { mutex } ;
2019-07-02 17:24:22 +00:00
for ( auto & name_info : infos )
if ( name_info . second . loaded ( ) )
2019-06-29 17:27:32 +00:00
return true ;
return false ;
}
2017-10-06 10:31:06 +00:00
2019-12-12 18:33:43 +00:00
Strings getAllTriedToLoadNames ( ) const
2019-06-02 12:11:01 +00:00
{
2020-01-23 13:35:12 +00:00
std : : lock_guard lock { mutex } ;
2019-12-12 18:33:43 +00:00
Strings names ;
for ( auto & [ name , info ] : infos )
if ( info . triedToLoad ( ) )
names . push_back ( name ) ;
return names ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-12-12 18:33:43 +00:00
/// Tries to load a specified object during the timeout.
template < typename ReturnType >
ReturnType tryLoad ( const String & name , Duration timeout )
2019-06-02 12:11:01 +00:00
{
std : : unique_lock lock { mutex } ;
2019-12-15 13:22:04 +00:00
Info * info = loadImpl ( name , timeout , false , lock ) ;
2019-06-02 12:11:01 +00:00
if ( ! info )
2019-12-12 18:33:43 +00:00
return notExists < ReturnType > ( name ) ;
return info - > getLoadResult < ReturnType > ( ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType tryLoad ( const FilterByNameFunction & filter , Duration timeout )
2019-06-02 12:11:01 +00:00
{
std : : unique_lock lock { mutex } ;
2019-12-15 13:22:04 +00:00
loadImpl ( filter , timeout , false , lock ) ;
2019-12-12 18:33:43 +00:00
return collectLoadResults < ReturnType > ( filter ) ;
2019-06-02 12:11:01 +00:00
}
2019-12-12 18:33:43 +00:00
/// Tries to load or reload a specified object.
template < typename ReturnType >
ReturnType tryLoadOrReload ( const String & name , Duration timeout )
2019-10-17 13:05:12 +00:00
{
std : : unique_lock lock { mutex } ;
2019-12-15 13:22:04 +00:00
Info * info = loadImpl ( name , timeout , true , lock ) ;
2019-06-02 12:11:01 +00:00
if ( ! info )
2019-12-12 18:33:43 +00:00
return notExists < ReturnType > ( name ) ;
return info - > getLoadResult < ReturnType > ( ) ;
2019-06-02 12:11:01 +00:00
}
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType tryLoadOrReload ( const FilterByNameFunction & filter , Duration timeout )
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
std : : unique_lock lock { mutex } ;
2019-12-15 13:22:04 +00:00
loadImpl ( filter , timeout , true , lock ) ;
2019-12-12 18:33:43 +00:00
return collectLoadResults < ReturnType > ( filter ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
/// Starts reloading all the object which update time is earlier than now.
/// The function doesn't touch the objects which were never tried to load.
void reloadOutdated ( )
{
2019-12-04 15:23:05 +00:00
/// Iterate through all the objects and find loaded ones which should be checked if they need update.
std : : unordered_map < LoadablePtr , bool > should_update_map ;
2019-07-16 17:47:18 +00:00
{
std : : lock_guard lock { mutex } ;
TimePoint now = std : : chrono : : system_clock : : now ( ) ;
for ( const auto & name_and_info : infos )
2019-06-02 12:11:01 +00:00
{
2019-07-16 17:47:18 +00:00
const auto & info = name_and_info . second ;
2019-12-15 13:22:04 +00:00
if ( ( now > = info . next_update_time ) & & ! info . is_loading ( ) & & info . loaded ( ) )
2019-12-04 16:20:24 +00:00
should_update_map . emplace ( info . object , info . failedToReload ( ) ) ;
2019-07-16 17:47:18 +00:00
}
}
2019-07-19 21:01:20 +00:00
/// Find out which of the loaded objects were modified.
2019-12-04 15:23:05 +00:00
/// We couldn't perform these checks while we were building `should_update_map` because
2019-09-27 12:36:54 +00:00
/// the `mutex` should be unlocked while we're calling the function object->isModified()
2019-12-04 15:23:05 +00:00
for ( auto & [ object , should_update_flag ] : should_update_map )
2019-07-16 17:47:18 +00:00
{
try
{
2020-01-11 09:50:41 +00:00
/// Maybe already true, if we have an exception
2019-12-04 15:23:05 +00:00
if ( ! should_update_flag )
should_update_flag = object - > isModified ( ) ;
2019-03-04 18:19:48 +00:00
}
2019-07-16 17:47:18 +00:00
catch ( . . . )
{
2019-12-25 23:12:12 +00:00
tryLogCurrentException ( log , " Could not check if " + type_name + " ' " + object - > getLoadableName ( ) + " ' was modified " ) ;
2019-12-04 15:23:05 +00:00
/// Cannot check isModified, so update
should_update_flag = true ;
2019-07-16 17:47:18 +00:00
}
}
2019-07-19 21:01:20 +00:00
/// Iterate through all the objects again and either start loading or just set `next_update_time`.
2019-07-16 17:47:18 +00:00
{
std : : lock_guard lock { mutex } ;
TimePoint now = std : : chrono : : system_clock : : now ( ) ;
for ( auto & [ name , info ] : infos )
2019-07-19 19:37:48 +00:00
{
2019-12-15 13:22:04 +00:00
if ( ( now > = info . next_update_time ) & & ! info . is_loading ( ) )
2019-07-16 17:47:18 +00:00
{
2019-07-19 21:03:25 +00:00
if ( info . loaded ( ) )
{
2019-12-04 15:23:05 +00:00
auto it = should_update_map . find ( info . object ) ;
if ( it = = should_update_map . end ( ) )
continue ; /// Object has been just loaded (it wasn't loaded while we were building the map `should_update_map`), so we don't have to reload it right now.
bool should_update_flag = it - > second ;
if ( ! should_update_flag )
2019-07-19 21:03:25 +00:00
{
2019-09-27 12:36:54 +00:00
info . next_update_time = calculateNextUpdateTime ( info . object , info . error_count ) ;
2019-07-19 21:03:25 +00:00
continue ;
}
2019-07-19 21:01:20 +00:00
2019-12-04 16:20:24 +00:00
/// Object was modified or it was failed to reload last time, so it should be reloaded.
2019-12-15 13:22:04 +00:00
startLoading ( info ) ;
2019-07-19 21:01:20 +00:00
}
else if ( info . failed ( ) )
{
/// Object was never loaded successfully and should be reloaded.
2019-12-15 13:22:04 +00:00
startLoading ( info ) ;
2019-07-19 21:03:25 +00:00
}
2019-07-16 17:47:18 +00:00
}
2019-07-19 19:37:48 +00:00
}
2019-07-16 17:47:18 +00:00
}
2018-12-30 01:09:06 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
private :
struct Info
2019-03-05 16:01:13 +00:00
{
2019-12-12 18:33:43 +00:00
Info ( const String & name_ , const ObjectConfig & object_config_ ) : name ( name_ ) , object_config ( object_config_ ) { }
2019-06-02 12:11:01 +00:00
bool loaded ( ) const { return object ! = nullptr ; }
bool failed ( ) const { return ! object & & exception ; }
2019-12-15 13:22:04 +00:00
bool loadedOrFailed ( ) const { return loaded ( ) | | failed ( ) ; }
bool triedToLoad ( ) const { return loaded ( ) | | failed ( ) | | is_loading ( ) ; }
2019-12-04 16:20:24 +00:00
bool failedToReload ( ) const { return loaded ( ) & & exception ! = nullptr ; }
2019-12-15 13:22:04 +00:00
bool is_loading ( ) const { return loading_id > state_id ; }
2019-06-02 12:11:01 +00:00
Status status ( ) const
{
if ( object )
2019-12-15 13:22:04 +00:00
return is_loading ( ) ? Status : : LOADED_AND_RELOADING : Status : : LOADED ;
2019-06-02 12:11:01 +00:00
else if ( exception )
2019-12-15 13:22:04 +00:00
return is_loading ( ) ? Status : : FAILED_AND_RELOADING : Status : : FAILED ;
2019-06-02 12:11:01 +00:00
else
2019-12-15 13:22:04 +00:00
return is_loading ( ) ? Status : : LOADING : Status : : NOT_LOADED ;
2019-06-02 12:11:01 +00:00
}
2019-09-27 12:36:54 +00:00
Duration loadingDuration ( ) const
2019-06-02 12:11:01 +00:00
{
2019-12-15 13:22:04 +00:00
if ( is_loading ( ) )
2019-06-02 12:11:01 +00:00
return std : : chrono : : duration_cast < Duration > ( std : : chrono : : system_clock : : now ( ) - loading_start_time ) ;
return std : : chrono : : duration_cast < Duration > ( loading_end_time - loading_start_time ) ;
}
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType getLoadResult ( ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
if constexpr ( std : : is_same_v < ReturnType , LoadResult > )
{
LoadResult result ;
result . name = name ;
result . status = status ( ) ;
result . object = object ;
result . exception = exception ;
result . loading_start_time = loading_start_time ;
2020-02-27 10:07:50 +00:00
result . last_successful_update_time = last_successful_update_time ;
2019-12-12 18:33:43 +00:00
result . loading_duration = loadingDuration ( ) ;
result . origin = object_config . path ;
result . repository_name = object_config . repository_name ;
return result ;
}
else
{
static_assert ( std : : is_same_v < ReturnType , ExternalLoader : : LoadablePtr > ) ;
return object ;
}
2019-06-02 12:11:01 +00:00
}
2019-12-12 18:33:43 +00:00
String name ;
2019-06-02 12:11:01 +00:00
LoadablePtr object ;
2019-12-12 18:33:43 +00:00
ObjectConfig object_config ;
2019-06-02 12:11:01 +00:00
TimePoint loading_start_time ;
TimePoint loading_end_time ;
2020-02-27 10:07:50 +00:00
TimePoint last_successful_update_time ;
2019-12-15 13:22:04 +00:00
size_t state_id = 0 ; /// Index of the current state of this `info`, this index is incremented every loading.
size_t loading_id = 0 ; /// The value which will be stored in `state_id` after finishing the current loading.
2019-06-02 12:11:01 +00:00
size_t error_count = 0 ; /// Numbers of errors since last successful loading.
std : : exception_ptr exception ; /// Last error occurred.
TimePoint next_update_time = TimePoint : : max ( ) ; /// Time of the next update, `TimePoint::max()` means "never".
2019-03-05 16:01:13 +00:00
} ;
2019-06-02 12:11:01 +00:00
Info * getInfo ( const String & name )
2018-12-30 01:09:06 +00:00
{
2019-06-02 12:11:01 +00:00
auto it = infos . find ( name ) ;
if ( it = = infos . end ( ) )
return nullptr ;
return & it - > second ;
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
const Info * getInfo ( const String & name ) const
{
auto it = infos . find ( name ) ;
if ( it = = infos . end ( ) )
return nullptr ;
return & it - > second ;
}
2019-12-12 18:33:43 +00:00
template < typename ReturnType >
ReturnType collectLoadResults ( const FilterByNameFunction & filter ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
ReturnType results ;
results . reserve ( infos . size ( ) ) ;
2019-06-02 12:11:01 +00:00
for ( const auto & [ name , info ] : infos )
2019-10-17 13:05:12 +00:00
{
2019-12-12 18:33:43 +00:00
if ( filter ( name ) )
{
auto result = info . template getLoadResult < typename ReturnType : : value_type > ( ) ;
if constexpr ( std : : is_same_v < typename ReturnType : : value_type , LoadablePtr > )
{
if ( ! result )
continue ;
}
results . emplace_back ( std : : move ( result ) ) ;
}
2019-10-17 13:05:12 +00:00
}
2019-12-12 18:33:43 +00:00
return results ;
2019-06-02 12:11:01 +00:00
}
2019-12-15 13:22:04 +00:00
Info * loadImpl ( const String & name , Duration timeout , bool forced_to_reload , std : : unique_lock < std : : mutex > & lock )
2019-06-02 12:11:01 +00:00
{
2019-12-15 13:22:04 +00:00
std : : optional < size_t > min_id ;
Info * info = nullptr ;
auto pred = [ & ]
2018-12-30 01:09:06 +00:00
{
2019-06-02 12:11:01 +00:00
info = getInfo ( name ) ;
2019-12-15 13:22:04 +00:00
if ( ! info )
return true ; /// stop
if ( ! min_id )
min_id = getMinIDToFinishLoading ( forced_to_reload ) ;
if ( info - > state_id > = min_id )
return true ; /// stop
if ( info - > loading_id < min_id )
startLoading ( * info , forced_to_reload , * min_id ) ;
return false ; /// wait for the next event
2019-06-02 12:11:01 +00:00
} ;
2019-12-12 18:33:43 +00:00
if ( timeout = = WAIT )
2019-06-02 12:11:01 +00:00
event . wait ( lock , pred ) ;
else
event . wait_for ( lock , timeout , pred ) ;
return info ;
}
2019-12-15 13:22:04 +00:00
void loadImpl ( const FilterByNameFunction & filter , Duration timeout , bool forced_to_reload , std : : unique_lock < std : : mutex > & lock )
2019-06-02 12:11:01 +00:00
{
2019-12-15 13:22:04 +00:00
std : : optional < size_t > min_id ;
auto pred = [ & ]
2019-06-02 12:11:01 +00:00
{
2019-12-15 13:22:04 +00:00
if ( ! min_id )
min_id = getMinIDToFinishLoading ( forced_to_reload ) ;
2019-06-02 12:11:01 +00:00
bool all_ready = true ;
for ( auto & [ name , info ] : infos )
2019-03-05 16:01:13 +00:00
{
2019-12-15 13:22:04 +00:00
if ( ! filter ( name ) )
2019-06-02 12:11:01 +00:00
continue ;
2019-12-15 13:22:04 +00:00
if ( info . state_id > = min_id )
continue ;
all_ready = false ;
if ( info . loading_id < min_id )
startLoading ( info , forced_to_reload , * min_id ) ;
2019-03-05 16:01:13 +00:00
}
2019-06-02 12:11:01 +00:00
return all_ready ;
} ;
2019-12-12 18:33:43 +00:00
if ( timeout = = WAIT )
2019-06-02 12:11:01 +00:00
event . wait ( lock , pred ) ;
else
event . wait_for ( lock , timeout , pred ) ;
}
2019-12-15 13:22:04 +00:00
/// When state_id >= getMinIDToFinishLoading() the loading is considered as finished.
size_t getMinIDToFinishLoading ( bool forced_to_reload ) const
2019-06-02 12:11:01 +00:00
{
2019-12-15 13:22:04 +00:00
if ( forced_to_reload )
{
/// We need to force reloading, that's why we return next_id_counter here
/// (because info.state_id < next_id_counter for any info).
return next_id_counter ;
}
/// The loading of an object can cause the loading of another object.
/// We use the same "min_id" in this case to allows reloading multiple objects at once
/// taking into account their dependencies.
auto it = min_id_to_finish_loading_dependencies . find ( std : : this_thread : : get_id ( ) ) ;
if ( it ! = min_id_to_finish_loading_dependencies . end ( ) )
return it - > second ;
/// We just need the first loading to be finished, that's why we return 1 here
/// (because info.state_id >= 1 since the first loading is finished, successfully or not).
return 1 ;
}
void startLoading ( Info & info , bool forced_to_reload = false , size_t min_id_to_finish_loading_dependencies_ = 1 )
{
if ( info . is_loading ( ) )
{
if ( ! forced_to_reload )
return ;
cancelLoading ( info ) ;
}
2019-06-02 12:11:01 +00:00
/// All loadings have unique loading IDs.
2019-12-15 13:22:04 +00:00
size_t loading_id = next_id_counter + + ;
2019-06-02 12:11:01 +00:00
info . loading_id = loading_id ;
info . loading_start_time = std : : chrono : : system_clock : : now ( ) ;
info . loading_end_time = TimePoint { } ;
2019-10-21 13:54:23 +00:00
if ( enable_async_loading )
2019-06-02 12:11:01 +00:00
{
/// Put a job to the thread pool for the loading.
2019-12-15 13:22:04 +00:00
auto thread = ThreadFromGlobalPool { & LoadingDispatcher : : doLoading , this , info . name , loading_id , forced_to_reload , min_id_to_finish_loading_dependencies_ , true } ;
loading_threads . try_emplace ( loading_id , std : : move ( thread ) ) ;
2017-10-06 10:31:06 +00:00
}
2019-06-02 12:11:01 +00:00
else
2017-10-06 10:31:06 +00:00
{
2019-06-02 12:11:01 +00:00
/// Perform the loading immediately.
2019-12-15 13:22:04 +00:00
doLoading ( info . name , loading_id , forced_to_reload , min_id_to_finish_loading_dependencies_ , false ) ;
2018-12-30 01:09:06 +00:00
}
2019-06-02 12:11:01 +00:00
}
2018-12-30 01:09:06 +00:00
2019-12-15 13:22:04 +00:00
void cancelLoading ( Info & info )
{
if ( ! info . is_loading ( ) )
return ;
2017-10-06 10:31:06 +00:00
2019-12-15 13:22:04 +00:00
/// In fact we cannot actually CANCEL the loading (because it's possibly already being performed in another thread).
/// But we can reset the `loading_id` and doLoading() will understand it as a signal to stop loading.
info . loading_id = info . state_id ;
info . loading_end_time = std : : chrono : : system_clock : : now ( ) ;
}
/// Does the loading, possibly in the separate thread.
void doLoading ( const String & name , size_t loading_id , bool forced_to_reload , size_t min_id_to_finish_loading_dependencies_ , bool async )
2019-10-22 12:57:58 +00:00
{
2017-10-06 10:31:06 +00:00
try
{
2019-12-15 13:22:04 +00:00
/// Prepare for loading.
std : : optional < Info > info ;
{
LoadingGuardForAsyncLoad lock ( async , mutex ) ;
info = prepareToLoadSingleObject ( name , loading_id , min_id_to_finish_loading_dependencies_ , lock ) ;
if ( ! info )
return ;
}
/// Previous version can be used as the base for new loading, enabling loading only part of data.
auto previous_version_as_base_for_loading = info - > object ;
if ( forced_to_reload )
previous_version_as_base_for_loading = nullptr ; /// Need complete reloading, cannot use the previous version.
/// Loading.
auto [ new_object , new_exception ] = loadSingleObject ( name , info - > object_config , previous_version_as_base_for_loading ) ;
if ( ! new_object & & ! new_exception )
throw Exception ( " No object created and no exception raised for " + type_name , ErrorCodes : : LOGICAL_ERROR ) ;
/// Saving the result of the loading.
{
LoadingGuardForAsyncLoad lock ( async , mutex ) ;
saveResultOfLoadingSingleObject ( name , loading_id , info - > object , new_object , new_exception , info - > error_count , lock ) ;
finishLoadingSingleObject ( name , loading_id , lock ) ;
}
event . notify_all ( ) ;
2017-10-06 10:31:06 +00:00
}
catch ( . . . )
{
2019-12-15 13:22:04 +00:00
LoadingGuardForAsyncLoad lock ( async , mutex ) ;
finishLoadingSingleObject ( name , loading_id , lock ) ;
throw ;
2019-06-02 12:11:01 +00:00
}
2019-10-22 12:57:58 +00:00
}
2017-10-06 10:31:06 +00:00
2019-12-15 13:22:04 +00:00
/// Returns single object info, checks loading_id and name.
std : : optional < Info > prepareToLoadSingleObject (
const String & name , size_t loading_id , size_t min_id_to_finish_loading_dependencies_ , const LoadingGuardForAsyncLoad & )
2019-10-22 12:57:58 +00:00
{
Info * info = getInfo ( name ) ;
2019-12-15 13:22:04 +00:00
/// We check here if this is exactly the same loading as we planned to perform.
/// This check is necessary because the object could be removed or load with another config before this thread even starts.
if ( ! info | | ! info - > is_loading ( ) | | ( info - > loading_id ! = loading_id ) )
2019-10-22 12:57:58 +00:00
return { } ;
2019-10-17 17:36:53 +00:00
2019-12-15 13:22:04 +00:00
min_id_to_finish_loading_dependencies [ std : : this_thread : : get_id ( ) ] = min_id_to_finish_loading_dependencies_ ;
2019-10-22 12:57:58 +00:00
return * info ;
}
2019-12-15 13:22:04 +00:00
/// Load one object, returns object ptr or exception.
std : : pair < LoadablePtr , std : : exception_ptr >
loadSingleObject ( const String & name , const ObjectConfig & config , LoadablePtr previous_version )
2019-10-22 13:41:17 +00:00
{
2019-12-15 13:22:04 +00:00
/// Use `create_function` to perform the actual loading.
/// It's much better to do it with `mutex` unlocked because the loading can take a lot of time
/// and require access to other objects.
LoadablePtr new_object ;
std : : exception_ptr new_exception ;
try
2019-10-22 13:41:17 +00:00
{
2019-12-15 13:22:04 +00:00
new_object = create_object ( name , config , previous_version ) ;
2019-10-22 13:41:17 +00:00
}
2019-12-15 13:22:04 +00:00
catch ( . . . )
{
new_exception = std : : current_exception ( ) ;
}
return std : : make_pair ( new_object , new_exception ) ;
2019-10-22 13:41:17 +00:00
}
2019-12-15 13:22:04 +00:00
/// Saves the result of the loading, calculates the time of the next update, and handles errors.
void saveResultOfLoadingSingleObject (
2019-10-22 12:57:58 +00:00
const String & name ,
size_t loading_id ,
LoadablePtr previous_version ,
LoadablePtr new_object ,
std : : exception_ptr new_exception ,
size_t error_count ,
2019-12-15 13:22:04 +00:00
const LoadingGuardForAsyncLoad & )
2019-10-22 12:57:58 +00:00
{
2019-06-02 12:11:01 +00:00
/// Calculate a new update time.
TimePoint next_update_time ;
try
{
if ( new_exception )
+ + error_count ;
else
error_count = 0 ;
2019-10-17 17:36:53 +00:00
2019-10-23 13:02:40 +00:00
LoadablePtr object = previous_version ;
if ( new_object )
object = new_object ;
next_update_time = calculateNextUpdateTime ( object , error_count ) ;
2019-06-02 12:11:01 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( log , " Cannot find out when the " + type_name + " ' " + name + " ' should be updated " ) ;
next_update_time = TimePoint : : max ( ) ;
2017-10-06 10:31:06 +00:00
}
2018-08-10 01:41:54 +00:00
2019-10-23 09:27:34 +00:00
2019-10-22 12:57:58 +00:00
Info * info = getInfo ( name ) ;
2019-06-02 12:11:01 +00:00
2019-12-15 13:22:04 +00:00
/// We should check if this is still the same loading as we were doing.
2019-06-02 12:11:01 +00:00
/// This is necessary because the object could be removed or load with another config while the `mutex` was unlocked.
2019-12-15 13:22:04 +00:00
if ( ! info | | ! info - > is_loading ( ) | | ( info - > loading_id ! = loading_id ) )
2019-06-02 12:11:01 +00:00
return ;
2019-10-23 09:40:09 +00:00
if ( new_exception )
2019-04-10 18:36:52 +00:00
{
2019-06-02 12:11:01 +00:00
auto next_update_time_description = [ next_update_time ]
{
if ( next_update_time = = TimePoint : : max ( ) )
return String ( ) ;
2019-11-09 19:14:51 +00:00
return " , next update is scheduled at " + ext : : to_string ( next_update_time ) ;
2019-06-02 12:11:01 +00:00
} ;
if ( previous_version )
tryLogException ( new_exception , log , " Could not update " + type_name + " ' " + name + " ' "
" , leaving the previous version " + next_update_time_description ( ) ) ;
else
tryLogException ( new_exception , log , " Could not load " + type_name + " ' " + name + " ' " + next_update_time_description ( ) ) ;
2019-04-10 18:36:52 +00:00
}
2019-06-02 12:11:01 +00:00
if ( new_object )
info - > object = new_object ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
info - > exception = new_exception ;
info - > error_count = error_count ;
2020-02-27 10:07:50 +00:00
const auto current_time = std : : chrono : : system_clock : : now ( ) ;
info - > loading_end_time = current_time ;
if ( ! info - > exception )
info - > last_successful_update_time = current_time ;
2019-12-15 13:22:04 +00:00
info - > state_id = info - > loading_id ;
2019-06-02 12:11:01 +00:00
info - > next_update_time = next_update_time ;
2019-10-22 12:57:58 +00:00
}
2019-04-15 19:53:46 +00:00
2019-12-15 13:22:04 +00:00
/// Removes the references to the loading thread from the maps.
void finishLoadingSingleObject ( const String & name , size_t loading_id , const LoadingGuardForAsyncLoad & )
2019-10-22 12:57:58 +00:00
{
2019-12-15 13:22:04 +00:00
Info * info = getInfo ( name ) ;
if ( info & & ( info - > loading_id = = loading_id ) )
info - > loading_id = info - > state_id ;
2019-10-22 12:57:58 +00:00
2019-12-15 13:22:04 +00:00
min_id_to_finish_loading_dependencies . erase ( std : : this_thread : : get_id ( ) ) ;
2019-10-22 12:57:58 +00:00
2019-12-15 13:22:04 +00:00
auto it = loading_threads . find ( loading_id ) ;
if ( it ! = loading_threads . end ( ) )
2019-10-22 13:41:17 +00:00
{
2019-12-15 13:22:04 +00:00
it - > second . detach ( ) ;
loading_threads . erase ( it ) ;
2019-10-22 13:41:17 +00:00
}
2019-06-02 12:11:01 +00:00
}
2019-09-27 12:36:54 +00:00
/// Calculate next update time for loaded_object. Can be called without mutex locking,
/// because single loadable can be loaded in single thread only.
TimePoint calculateNextUpdateTime ( const LoadablePtr & loaded_object , size_t error_count ) const
{
static constexpr auto never = TimePoint : : max ( ) ;
2019-10-23 13:02:40 +00:00
if ( loaded_object )
2019-09-27 12:36:54 +00:00
{
if ( ! loaded_object - > supportUpdates ( ) )
return never ;
/// do not update loadable objects with zero as lifetime
const auto & lifetime = loaded_object - > getLifetime ( ) ;
2019-12-02 13:05:43 +00:00
if ( lifetime . min_sec = = 0 & & lifetime . max_sec = = 0 )
2019-09-27 12:36:54 +00:00
return never ;
2019-10-23 13:02:40 +00:00
if ( ! error_count )
{
std : : uniform_int_distribution < UInt64 > distribution { lifetime . min_sec , lifetime . max_sec } ;
return std : : chrono : : system_clock : : now ( ) + std : : chrono : : seconds { distribution ( rnd_engine ) } ;
}
2019-09-27 12:36:54 +00:00
}
return std : : chrono : : system_clock : : now ( ) + std : : chrono : : seconds ( calculateDurationWithBackoff ( rnd_engine , error_count ) ) ;
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
const CreateObjectFunction create_object ;
const String type_name ;
Logger * log ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
mutable std : : mutex mutex ;
std : : condition_variable event ;
2019-09-30 11:18:01 +00:00
ObjectConfigsPtr configs ;
2019-06-02 12:11:01 +00:00
std : : unordered_map < String , Info > infos ;
bool always_load_everything = false ;
2019-10-18 15:44:32 +00:00
std : : atomic < bool > enable_async_loading = false ;
2019-12-15 13:22:04 +00:00
std : : unordered_map < size_t , ThreadFromGlobalPool > loading_threads ;
std : : unordered_map < std : : thread : : id , size_t > min_id_to_finish_loading_dependencies ;
size_t next_id_counter = 1 ; /// should always be > 0
2019-09-27 12:36:54 +00:00
mutable pcg64 rnd_engine { randomSeed ( ) } ;
2019-06-02 12:11:01 +00:00
} ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
class ExternalLoader : : PeriodicUpdater : private boost : : noncopyable
{
public :
2019-08-30 09:50:38 +00:00
static constexpr UInt64 check_period_sec = 5 ;
2019-09-30 16:12:08 +00:00
PeriodicUpdater ( LoadablesConfigReader & config_files_reader_ , LoadingDispatcher & loading_dispatcher_ )
2019-06-02 12:11:01 +00:00
: config_files_reader ( config_files_reader_ ) , loading_dispatcher ( loading_dispatcher_ )
{
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
~ PeriodicUpdater ( ) { enable ( false ) ; }
2017-10-06 10:31:06 +00:00
2019-08-30 09:50:38 +00:00
void enable ( bool enable_ )
2019-06-02 12:11:01 +00:00
{
std : : unique_lock lock { mutex } ;
enabled = enable_ ;
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
if ( enable_ )
{
if ( ! thread . joinable ( ) )
{
/// Starts the thread which will do periodic updates.
thread = ThreadFromGlobalPool { & PeriodicUpdater : : doPeriodicUpdates , this } ;
}
}
else
{
if ( thread . joinable ( ) )
{
/// Wait for the thread to finish.
auto temp_thread = std : : move ( thread ) ;
lock . unlock ( ) ;
event . notify_one ( ) ;
temp_thread . join ( ) ;
2019-04-10 18:36:52 +00:00
}
}
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
private :
void doPeriodicUpdates ( )
{
setThreadName ( " ExterLdrReload " ) ;
std : : unique_lock lock { mutex } ;
auto pred = [ this ] { return ! enabled ; } ;
2019-08-30 09:50:38 +00:00
while ( ! event . wait_for ( lock , std : : chrono : : seconds ( check_period_sec ) , pred ) )
2019-06-02 12:11:01 +00:00
{
lock . unlock ( ) ;
loading_dispatcher . setConfiguration ( config_files_reader . read ( ) ) ;
loading_dispatcher . reloadOutdated ( ) ;
lock . lock ( ) ;
}
}
2019-04-15 19:53:46 +00:00
2019-09-30 16:12:08 +00:00
LoadablesConfigReader & config_files_reader ;
2019-06-02 12:11:01 +00:00
LoadingDispatcher & loading_dispatcher ;
mutable std : : mutex mutex ;
bool enabled = false ;
ThreadFromGlobalPool thread ;
std : : condition_variable event ;
} ;
2019-12-12 18:33:43 +00:00
ExternalLoader : : ExternalLoader ( const String & type_name_ , Logger * log_ )
: config_files_reader ( std : : make_unique < LoadablesConfigReader > ( type_name_ , log_ ) )
2019-06-02 12:11:01 +00:00
, loading_dispatcher ( std : : make_unique < LoadingDispatcher > (
2019-12-04 23:53:06 +00:00
std : : bind ( & ExternalLoader : : createObject , this , std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 ) ,
2019-06-02 12:11:01 +00:00
type_name_ ,
2019-12-12 18:33:43 +00:00
log_ ) )
2019-06-02 12:11:01 +00:00
, periodic_updater ( std : : make_unique < PeriodicUpdater > ( * config_files_reader , * loading_dispatcher ) )
, type_name ( type_name_ )
2019-12-12 18:33:43 +00:00
, log ( log_ )
2019-06-02 12:11:01 +00:00
{
2019-04-15 19:53:46 +00:00
}
2019-06-02 12:11:01 +00:00
ExternalLoader : : ~ ExternalLoader ( ) = default ;
2019-04-15 19:53:46 +00:00
2019-12-30 23:30:06 +00:00
ext : : scope_guard ExternalLoader : : addConfigRepository ( std : : unique_ptr < IExternalLoaderConfigRepository > repository )
2019-10-15 18:04:17 +00:00
{
2019-12-30 23:30:06 +00:00
auto * ptr = repository . get ( ) ;
String name = ptr - > getName ( ) ;
config_files_reader - > addConfigRepository ( std : : move ( repository ) ) ;
reloadConfig ( name ) ;
return [ this , ptr , name ] ( )
{
config_files_reader - > removeConfigRepository ( ptr ) ;
reloadConfig ( name ) ;
} ;
2019-10-15 18:04:17 +00:00
}
2019-12-30 23:30:06 +00:00
void ExternalLoader : : setConfigSettings ( const ExternalLoaderConfigSettings & settings )
2019-04-15 19:53:46 +00:00
{
2019-12-30 23:30:06 +00:00
config_files_reader - > setConfigSettings ( settings ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
void ExternalLoader : : enableAlwaysLoadEverything ( bool enable )
{
loading_dispatcher - > enableAlwaysLoadEverything ( enable ) ;
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
void ExternalLoader : : enableAsyncLoading ( bool enable )
{
loading_dispatcher - > enableAsyncLoading ( enable ) ;
2019-04-10 18:36:52 +00:00
}
2017-10-06 10:31:06 +00:00
2019-08-30 09:50:38 +00:00
void ExternalLoader : : enablePeriodicUpdates ( bool enable_ )
2019-06-02 12:11:01 +00:00
{
2019-08-30 09:50:38 +00:00
periodic_updater - > enable ( enable_ ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-29 17:27:32 +00:00
bool ExternalLoader : : hasCurrentlyLoadedObjects ( ) const
2019-06-02 12:11:01 +00:00
{
2019-06-29 17:27:32 +00:00
return loading_dispatcher - > hasCurrentlyLoadedObjects ( ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-06-02 12:11:01 +00:00
ExternalLoader : : Status ExternalLoader : : getCurrentStatus ( const String & name ) const
{
return loading_dispatcher - > getCurrentStatus ( name ) ;
2019-04-10 18:36:52 +00:00
}
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : getCurrentLoadResult ( const String & name ) const
2019-04-10 18:36:52 +00:00
{
2019-12-12 18:33:43 +00:00
return loading_dispatcher - > getCurrentLoadResult < ReturnType > ( name ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-15 19:53:46 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : getCurrentLoadResults ( const FilterByNameFunction & filter ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
return loading_dispatcher - > getCurrentLoadResults < ReturnType > ( filter ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-06-02 12:11:01 +00:00
ExternalLoader : : Loadables ExternalLoader : : getCurrentlyLoadedObjects ( ) const
{
2019-12-12 18:33:43 +00:00
return getCurrentLoadResults < Loadables > ( ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-12-12 18:33:43 +00:00
ExternalLoader : : Loadables ExternalLoader : : getCurrentlyLoadedObjects ( const FilterByNameFunction & filter ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
return getCurrentLoadResults < Loadables > ( filter ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-06-02 12:11:01 +00:00
size_t ExternalLoader : : getNumberOfCurrentlyLoadedObjects ( ) const
{
return loading_dispatcher - > getNumberOfCurrentlyLoadedObjects ( ) ;
}
2019-04-10 18:36:52 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : tryLoad ( const String & name , Duration timeout ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
return loading_dispatcher - > tryLoad < ReturnType > ( name , timeout ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : tryLoad ( const FilterByNameFunction & filter , Duration timeout ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
return loading_dispatcher - > tryLoad < ReturnType > ( filter , timeout ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : load ( const String & name ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
auto result = tryLoad < LoadResult > ( name ) ;
checkLoaded ( result , false ) ;
return convertTo < ReturnType > ( result ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : load ( const FilterByNameFunction & filter ) const
{
auto results = tryLoad < LoadResults > ( filter ) ;
checkLoaded ( results , false ) ;
return convertTo < ReturnType > ( results ) ;
}
2019-10-17 13:05:12 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : loadOrReload ( const String & name ) const
2019-10-17 13:05:12 +00:00
{
2019-12-12 18:33:43 +00:00
loading_dispatcher - > setConfiguration ( config_files_reader - > read ( ) ) ;
auto result = loading_dispatcher - > tryLoadOrReload < LoadResult > ( name , WAIT ) ;
checkLoaded ( result , true ) ;
return convertTo < ReturnType > ( result ) ;
2019-10-17 13:05:12 +00:00
}
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : loadOrReload ( const FilterByNameFunction & filter ) const
{
loading_dispatcher - > setConfiguration ( config_files_reader - > read ( ) ) ;
auto results = loading_dispatcher - > tryLoadOrReload < LoadResults > ( filter , WAIT ) ;
checkLoaded ( results , true ) ;
return convertTo < ReturnType > ( results ) ;
}
2019-10-17 13:05:12 +00:00
2019-12-12 18:33:43 +00:00
template < typename ReturnType , typename >
ReturnType ExternalLoader : : reloadAllTriedToLoad ( ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
std : : unordered_set < String > names ;
boost : : range : : copy ( getAllTriedToLoadNames ( ) , std : : inserter ( names , names . end ( ) ) ) ;
return loadOrReload < ReturnType > ( [ & names ] ( const String & name ) { return names . count ( name ) ; } ) ;
2019-06-02 12:11:01 +00:00
}
2019-04-10 18:36:52 +00:00
2019-12-12 18:33:43 +00:00
Strings ExternalLoader : : getAllTriedToLoadNames ( ) const
2019-06-02 12:11:01 +00:00
{
2019-12-12 18:33:43 +00:00
return loading_dispatcher - > getAllTriedToLoadNames ( ) ;
2017-10-06 10:31:06 +00:00
}
2019-12-12 18:33:43 +00:00
void ExternalLoader : : checkLoaded ( const ExternalLoader : : LoadResult & result ,
bool check_no_errors ) const
2017-10-06 10:31:06 +00:00
{
2019-12-12 18:33:43 +00:00
if ( result . object & & ( ! check_no_errors | | ! result . exception ) )
return ;
if ( result . status = = ExternalLoader : : Status : : LOADING )
throw Exception ( type_name + " ' " + result . name + " ' is still loading " , ErrorCodes : : BAD_ARGUMENTS ) ;
if ( result . exception )
2019-12-15 13:22:04 +00:00
std : : rethrow_exception ( result . exception ) ;
2019-12-12 18:33:43 +00:00
if ( result . status = = ExternalLoader : : Status : : NOT_EXIST )
throw Exception ( type_name + " ' " + result . name + " ' not found " , ErrorCodes : : BAD_ARGUMENTS ) ;
if ( result . status = = ExternalLoader : : Status : : NOT_LOADED )
throw Exception ( type_name + " ' " + result . name + " ' not tried to load " , ErrorCodes : : BAD_ARGUMENTS ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-12-12 18:33:43 +00:00
void ExternalLoader : : checkLoaded ( const ExternalLoader : : LoadResults & results ,
bool check_no_errors ) const
2019-11-25 22:48:23 +00:00
{
2019-12-12 18:33:43 +00:00
std : : exception_ptr exception ;
for ( const auto & result : results )
{
try
{
checkLoaded ( result , check_no_errors ) ;
}
catch ( . . . )
{
if ( ! exception )
exception = std : : current_exception ( ) ;
else
tryLogCurrentException ( log ) ;
}
}
if ( exception )
std : : rethrow_exception ( exception ) ;
2019-11-25 22:48:23 +00:00
}
2019-12-12 18:33:43 +00:00
2019-12-12 16:41:41 +00:00
void ExternalLoader : : reloadConfig ( ) const
2019-10-18 15:44:32 +00:00
{
2019-12-12 16:41:41 +00:00
loading_dispatcher - > setConfiguration ( config_files_reader - > read ( ) ) ;
2019-10-18 15:44:32 +00:00
}
2019-12-12 16:41:41 +00:00
void ExternalLoader : : reloadConfig ( const String & repository_name ) const
{
loading_dispatcher - > setConfiguration ( config_files_reader - > read ( repository_name ) ) ;
}
void ExternalLoader : : reloadConfig ( const String & repository_name , const String & path ) const
{
loading_dispatcher - > setConfiguration ( config_files_reader - > read ( repository_name , path ) ) ;
}
2019-10-18 15:44:32 +00:00
2019-07-17 08:39:36 +00:00
ExternalLoader : : LoadablePtr ExternalLoader : : createObject (
2019-12-04 23:53:06 +00:00
const String & name , const ObjectConfig & config , const LoadablePtr & previous_version ) const
2019-06-02 12:11:01 +00:00
{
2019-12-04 23:53:06 +00:00
if ( previous_version )
2019-07-17 08:39:36 +00:00
return previous_version - > clone ( ) ;
2017-10-06 10:31:06 +00:00
2019-12-11 11:09:21 +00:00
return create ( name , * config . config , config . key_in_config , config . repository_name ) ;
2019-06-02 12:11:01 +00:00
}
2017-10-06 10:31:06 +00:00
2019-06-02 12:11:01 +00:00
std : : vector < std : : pair < String , Int8 > > ExternalLoader : : getStatusEnumAllPossibleValues ( )
2018-03-23 19:56:24 +00:00
{
2019-06-02 12:11:01 +00:00
return std : : vector < std : : pair < String , Int8 > > {
{ toString ( Status : : NOT_LOADED ) , static_cast < Int8 > ( Status : : NOT_LOADED ) } ,
{ toString ( Status : : LOADED ) , static_cast < Int8 > ( Status : : LOADED ) } ,
{ toString ( Status : : FAILED ) , static_cast < Int8 > ( Status : : FAILED ) } ,
{ toString ( Status : : LOADING ) , static_cast < Int8 > ( Status : : LOADING ) } ,
{ toString ( Status : : LOADED_AND_RELOADING ) , static_cast < Int8 > ( Status : : LOADED_AND_RELOADING ) } ,
{ toString ( Status : : FAILED_AND_RELOADING ) , static_cast < Int8 > ( Status : : FAILED_AND_RELOADING ) } ,
{ toString ( Status : : NOT_EXIST ) , static_cast < Int8 > ( Status : : NOT_EXIST ) } ,
} ;
2018-03-23 19:56:24 +00:00
}
2019-06-02 12:11:01 +00:00
String toString ( ExternalLoader : : Status status )
2018-03-23 19:56:24 +00:00
{
2019-06-02 12:11:01 +00:00
using Status = ExternalLoader : : Status ;
switch ( status )
{
case Status : : NOT_LOADED : return " NOT_LOADED " ;
case Status : : LOADED : return " LOADED " ;
case Status : : FAILED : return " FAILED " ;
case Status : : LOADING : return " LOADING " ;
case Status : : FAILED_AND_RELOADING : return " FAILED_AND_RELOADING " ;
case Status : : LOADED_AND_RELOADING : return " LOADED_AND_RELOADING " ;
case Status : : NOT_EXIST : return " NOT_EXIST " ;
}
__builtin_unreachable ( ) ;
2018-03-23 19:56:24 +00:00
}
2019-06-02 12:11:01 +00:00
std : : ostream & operator < < ( std : : ostream & out , ExternalLoader : : Status status )
2017-10-06 11:10:01 +00:00
{
2019-06-02 12:11:01 +00:00
return out < < toString ( status ) ;
2017-10-06 11:10:01 +00:00
}
2019-12-12 18:33:43 +00:00
template ExternalLoader : : LoadablePtr ExternalLoader : : getCurrentLoadResult < ExternalLoader : : LoadablePtr > ( const String & ) const ;
template ExternalLoader : : LoadResult ExternalLoader : : getCurrentLoadResult < ExternalLoader : : LoadResult > ( const String & ) const ;
template ExternalLoader : : Loadables ExternalLoader : : getCurrentLoadResults < ExternalLoader : : Loadables > ( const FilterByNameFunction & ) const ;
template ExternalLoader : : LoadResults ExternalLoader : : getCurrentLoadResults < ExternalLoader : : LoadResults > ( const FilterByNameFunction & ) const ;
template ExternalLoader : : LoadablePtr ExternalLoader : : tryLoad < ExternalLoader : : LoadablePtr > ( const String & , Duration ) const ;
template ExternalLoader : : LoadResult ExternalLoader : : tryLoad < ExternalLoader : : LoadResult > ( const String & , Duration ) const ;
template ExternalLoader : : Loadables ExternalLoader : : tryLoad < ExternalLoader : : Loadables > ( const FilterByNameFunction & , Duration ) const ;
template ExternalLoader : : LoadResults ExternalLoader : : tryLoad < ExternalLoader : : LoadResults > ( const FilterByNameFunction & , Duration ) const ;
template ExternalLoader : : LoadablePtr ExternalLoader : : load < ExternalLoader : : LoadablePtr > ( const String & ) const ;
template ExternalLoader : : LoadResult ExternalLoader : : load < ExternalLoader : : LoadResult > ( const String & ) const ;
template ExternalLoader : : Loadables ExternalLoader : : load < ExternalLoader : : Loadables > ( const FilterByNameFunction & ) const ;
template ExternalLoader : : LoadResults ExternalLoader : : load < ExternalLoader : : LoadResults > ( const FilterByNameFunction & ) const ;
template ExternalLoader : : LoadablePtr ExternalLoader : : loadOrReload < ExternalLoader : : LoadablePtr > ( const String & ) const ;
template ExternalLoader : : LoadResult ExternalLoader : : loadOrReload < ExternalLoader : : LoadResult > ( const String & ) const ;
template ExternalLoader : : Loadables ExternalLoader : : loadOrReload < ExternalLoader : : Loadables > ( const FilterByNameFunction & ) const ;
template ExternalLoader : : LoadResults ExternalLoader : : loadOrReload < ExternalLoader : : LoadResults > ( const FilterByNameFunction & ) const ;
template ExternalLoader : : Loadables ExternalLoader : : reloadAllTriedToLoad < ExternalLoader : : Loadables > ( ) const ;
template ExternalLoader : : LoadResults ExternalLoader : : reloadAllTriedToLoad < ExternalLoader : : LoadResults > ( ) const ;
2017-10-06 10:31:06 +00:00
}