/CCGOnline/CCGOnline/IPDatabase/CompoundDatabaseTaskBatch.h

https://github.com/bretambrose/CCGOnlinePublic · C Header · 274 lines · 193 code · 61 blank · 20 comment · 33 complexity · bd8ab774935c11839de3f0eacebbd7fc MD5 · raw file

  1. /**********************************************************************************************************************
  2. (c) Copyright 2012, Bret Ambrose (mailto:bretambrose@gmail.com).
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. **********************************************************************************************************************/
  14. #pragma once
  15. #include "Interfaces/CompoundDatabaseTaskBatchInterface.h"
  16. #include "Interfaces/DatabaseTaskBaseInterface.h"
  17. #include "DatabaseCallContext.h"
  18. #include "DatabaseTaskBatchUtilities.h"
  19. #include "IPShared/Logging/LogInterface.h"
  20. namespace IP
  21. {
  22. namespace Db
  23. {
  24. class ICompoundDatabaseTask;
  25. template< typename T >
  26. class TCompoundDatabaseTaskBatch : public ICompoundDatabaseTaskBatch
  27. {
  28. public:
  29. using BASECLASS = ICompoundDatabaseTaskBatch;
  30. TCompoundDatabaseTaskBatch( void ) :
  31. BASECLASS(),
  32. ChildOrdering(),
  33. ChildCallContexts(),
  34. PendingTasks(),
  35. TaskName( typeid( T ).name() )
  36. {
  37. T::Register_Child_Tasks( this );
  38. }
  39. virtual ~TCompoundDatabaseTaskBatch()
  40. {
  41. FATAL_ASSERT( PendingTasks.empty() );
  42. std::for_each( ChildCallContexts.begin(), ChildCallContexts.end(), []( ChildCallContextPair &pair ){ delete pair.second; } );
  43. ChildCallContexts.clear();
  44. ChildOrdering.clear();
  45. }
  46. virtual Loki::TypeInfo Get_Task_Type_Info( void ) const
  47. {
  48. return Loki::TypeInfo( typeid( T ) );
  49. }
  50. virtual void Add_Task( IDatabaseTask * /*task*/ ) { FATAL_ASSERT( false ); }
  51. virtual void Add_Task( ICompoundDatabaseTask *task ) { PendingTasks.push_back( static_cast< ICompoundDatabaseTask* >( task ) ); }
  52. virtual void Execute_Tasks( IDatabaseConnection *connection, DBTaskBaseListType &successful_tasks, DBTaskBaseListType &failed_tasks )
  53. {
  54. successful_tasks.clear();
  55. failed_tasks.clear();
  56. if ( PendingTasks.empty() )
  57. {
  58. return;
  59. }
  60. LOG( IP::Logging::ELogLevel::LL_LOW, "CompoundDatabaseTaskBatch " << TaskName.c_str() << " - TaskCount: " << PendingTasks.size() );
  61. while ( !PendingTasks.empty() )
  62. {
  63. DBCompoundTaskListType sub_list;
  64. auto splice_iter = PendingTasks.begin();
  65. std::advance( splice_iter, std::min( static_cast< uint32_t >( PendingTasks.size() ), T::CompoundTaskBatchSize ) );
  66. sub_list.splice( sub_list.end(), PendingTasks, PendingTasks.begin(), splice_iter );
  67. Process_Parent_Task_List( connection, sub_list, successful_tasks, failed_tasks );
  68. }
  69. LOG( IP::Logging::ELogLevel::LL_LOW, "CompoundDatabaseTaskBatch " << TaskName.c_str() << " - Successes: " << successful_tasks.size() << ", Failures: " << failed_tasks.size() );
  70. }
  71. virtual void Register_Child_Variable_Sets( const Loki::TypeInfo &type_info, IDatabaseCallContext *child_call_context )
  72. {
  73. FATAL_ASSERT( ChildCallContexts.find( type_info ) == ChildCallContexts.end() );
  74. ChildOrdering.push_back( type_info );
  75. ChildCallContexts[ type_info ] = child_call_context;
  76. }
  77. private:
  78. void Process_Parent_Task_List( IDatabaseConnection *connection, DBCompoundTaskListType &sub_list, DBTaskBaseListType &successful_tasks, DBTaskBaseListType &failed_tasks )
  79. {
  80. while ( !sub_list.empty() )
  81. {
  82. std::for_each( sub_list.cbegin(), sub_list.cend(), []( ICompoundDatabaseTask *task ) { task->Clear_Child_Tasks(); task->Seed_Child_Tasks(); } );
  83. EDatabaseTaskIDType bad_task = EDatabaseTaskIDType::INVALID;
  84. EExecuteDBTaskListResult process_child_result = EExecuteDBTaskListResult::SUCCESS;
  85. for ( auto tt_iter = ChildOrdering.cbegin(), end = ChildOrdering.cend(); tt_iter != end; ++tt_iter )
  86. {
  87. DBTaskListType child_list;
  88. std::for_each( sub_list.cbegin(), sub_list.cend(), [ &tt_iter, &child_list ]( ICompoundDatabaseTask *task ) { task->Get_Child_Tasks_Of_Type( *tt_iter, child_list ); } );
  89. if( child_list.empty() )
  90. {
  91. continue;
  92. }
  93. LOG( IP::Logging::ELogLevel::LL_LOW, "CompoundDatabaseTaskBatch " << TaskName.c_str() << ", ChildTask " << tt_iter->name() << " - TaskCount: " << child_list.size() );
  94. auto context_iter = ChildCallContexts.find( *tt_iter );
  95. FATAL_ASSERT( context_iter != ChildCallContexts.end() );
  96. IDatabaseCallContext *call_context = context_iter->second;
  97. process_child_result = Process_Child_Task_List( call_context, connection, child_list, bad_task );
  98. if ( process_child_result != EExecuteDBTaskListResult::SUCCESS )
  99. {
  100. break;
  101. }
  102. std::for_each( sub_list.begin(), sub_list.end(), [&tt_iter] ( ICompoundDatabaseTask *task ) { task->On_Child_Task_Success( *tt_iter );});
  103. }
  104. // commit/rollback etc...
  105. if(process_child_result == EExecuteDBTaskListResult::SUCCESS)
  106. {
  107. connection->End_Transaction( true );
  108. // can't use splice due to type difference
  109. std::for_each( sub_list.begin(), sub_list.end(), [ &successful_tasks ]( ICompoundDatabaseTask *task ){ successful_tasks.push_back( task ); } );
  110. sub_list.clear();
  111. }
  112. else
  113. {
  114. connection->End_Transaction( false );
  115. if ( sub_list.size() == 1 )
  116. {
  117. failed_tasks.push_back( *( sub_list.begin() ) );
  118. sub_list.clear();
  119. return;
  120. }
  121. if ( bad_task != EDatabaseTaskIDType::INVALID )
  122. {
  123. auto find_iter = std::find_if( sub_list.begin(), sub_list.end(), [bad_task]( ICompoundDatabaseTask *task ){ return task->Get_ID() == bad_task; } );
  124. if ( find_iter != sub_list.end() )
  125. {
  126. failed_tasks.push_back( *find_iter );
  127. sub_list.erase( find_iter );
  128. }
  129. else
  130. {
  131. bad_task = EDatabaseTaskIDType::INVALID;
  132. }
  133. }
  134. if ( bad_task == EDatabaseTaskIDType::INVALID )
  135. {
  136. for ( auto iter = sub_list.begin(); iter != sub_list.end(); ++iter )
  137. {
  138. DBCompoundTaskListType new_sub_list;
  139. new_sub_list.push_back( *iter );
  140. Process_Parent_Task_List( connection, new_sub_list, successful_tasks, failed_tasks );
  141. }
  142. return;
  143. }
  144. }
  145. }
  146. }
  147. EExecuteDBTaskListResult Process_Child_Task_List( IDatabaseCallContext *call_context, IDatabaseConnection *connection, const DBTaskListType &child_list, EDatabaseTaskIDType &bad_task_id )
  148. {
  149. if ( child_list.empty() )
  150. {
  151. return EExecuteDBTaskListResult::SUCCESS;
  152. }
  153. if ( call_context->Get_Statement_Text().size() == 0 )
  154. {
  155. IDatabaseTask *first_task = *child_list.begin();
  156. FATAL_ASSERT( connection->Validate_Input_Output_Signatures( first_task, call_context->Get_Param_Rows(), call_context->Get_Result_Rows() ) );
  157. std::wstring statement_text;
  158. connection->Construct_Statement_Text( first_task, call_context->Get_Param_Rows(), statement_text );
  159. call_context->Set_Statement_Text( statement_text );
  160. }
  161. IDatabaseStatement *statement = connection->Allocate_Statement( call_context->Get_Statement_Text() );
  162. FATAL_ASSERT( statement != nullptr );
  163. if ( statement->Needs_Binding() )
  164. {
  165. statement->Bind_Input( call_context->Get_Param_Rows(), call_context->Get_Sizeof_Param_Type() );
  166. statement->Bind_Output( call_context->Get_Result_Rows(), call_context->Get_Sizeof_Result_Type(), call_context->Get_Result_Row_Count() );
  167. }
  168. FATAL_ASSERT( statement->Is_Ready_For_Use() );
  169. DBTaskListType child_list_copy;
  170. std::copy( child_list.begin(), child_list.end(), back_inserter(child_list_copy) );
  171. while ( !child_list_copy.empty() )
  172. {
  173. DBTaskListType sub_list;
  174. auto end_of_splice_iter = child_list_copy.begin();
  175. std::advance( end_of_splice_iter, std::min( static_cast<uint32_t>(child_list_copy.size()), call_context->Get_Param_Row_Count() ) );
  176. sub_list.splice( sub_list.end(), child_list_copy, child_list_copy.begin(), end_of_splice_iter );
  177. EExecuteDBTaskListResult result = EExecuteDBTaskListResult::SUCCESS;
  178. DBTaskListType::const_iterator failure_iter = sub_list.end();
  179. Execute_Task_List( call_context, statement, sub_list, result, failure_iter );
  180. if ( result != EExecuteDBTaskListResult::SUCCESS )
  181. {
  182. if ( result == EExecuteDBTaskListResult::FAILED_SPECIFIC_TASK )
  183. {
  184. bad_task_id = ( *failure_iter )->Get_ID();
  185. }
  186. return result;
  187. }
  188. }
  189. FATAL_ASSERT( statement->Is_Ready_For_Use() );
  190. connection->Release_Statement( statement );
  191. return EExecuteDBTaskListResult::SUCCESS;
  192. }
  193. using ChildTypeVector = std::vector< Loki::TypeInfo >;
  194. using ChildCallContextTable = std::unordered_map< Loki::TypeInfo, IDatabaseCallContext *, STypeInfoContainerHelper >;
  195. using ChildCallContextPair = std::pair< const Loki::TypeInfo, IDatabaseCallContext * >;
  196. ChildTypeVector ChildOrdering;
  197. ChildCallContextTable ChildCallContexts;
  198. DBCompoundTaskListType PendingTasks;
  199. std::string TaskName;
  200. };
  201. template< typename T >
  202. void Register_Database_Child_Task_Type( ICompoundDatabaseTaskBatch *compound_batch )
  203. {
  204. IDatabaseCallContext *child_call_context = new CDatabaseCallContext< T::InputParametersType, T::InputParameterBatchSize, T::ResultSetType, T::ResultSetBatchSize >();
  205. compound_batch->Register_Child_Variable_Sets( Loki::TypeInfo( typeid( T ) ), child_call_context );
  206. }
  207. } // namespace Db
  208. } // namespace IP