Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

926 lines
31 KiB

  1. //====== Copyright Valve Corporation, All rights reserved. ====================
  2. //
  3. // Purpose: Serialized Digital Object caching and manipulation
  4. //
  5. //=============================================================================
  6. #ifndef SBOCACHE_H
  7. #define SBOCACHE_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "tier1/utlhashmaplarge.h"
  12. #include "tier1/utlqueue.h"
  13. #include "tier1/utlvector.h"
  14. namespace GCSDK
  15. {
  16. // Call to register SDOs. All SDO types must be registered before loaded
  17. #define REG_SDO( classname ) GSDOCache().RegisterSDO( classname::k_eType, #classname )
  18. //-----------------------------------------------------------------------------
  19. // Purpose: Keeps a moving average of a data set
  20. //-----------------------------------------------------------------------------
  21. template< int SAMPLES >
  22. class CMovingAverage
  23. {
  24. public:
  25. CMovingAverage()
  26. {
  27. Reset();
  28. }
  29. void Reset()
  30. {
  31. memset( m_rglSamples, 0, sizeof( m_rglSamples ) );
  32. m_cSamples = 0;
  33. m_lTotal = 0;
  34. }
  35. void AddSample( int64 lSample )
  36. {
  37. int iIndex = m_cSamples % SAMPLES;
  38. m_lTotal += ( lSample - m_rglSamples[iIndex] );
  39. m_rglSamples[iIndex] = lSample;
  40. m_cSamples++;
  41. }
  42. uint64 GetAveragedSample() const
  43. {
  44. if ( !m_cSamples )
  45. return 0;
  46. int64 iMax = (int64)MIN( m_cSamples, SAMPLES );
  47. return m_lTotal / iMax;
  48. }
  49. private:
  50. int64 m_rglSamples[SAMPLES];
  51. int64 m_lTotal;
  52. uint64 m_cSamples;
  53. };
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Global accessor to the manager
  56. //-----------------------------------------------------------------------------
  57. class CSDOCache;
  58. CSDOCache &GSDOCache();
  59. //-----------------------------------------------------------------------------
  60. // Purpose: interface to a Database Backed Object
  61. //-----------------------------------------------------------------------------
  62. class ISDO
  63. {
  64. public:
  65. virtual ~ISDO() {}
  66. // Identification
  67. virtual int GetType() const = 0;
  68. virtual uint32 GetHashCode() const = 0;
  69. virtual bool IsEqual( const ISDO *pSDO ) const = 0;
  70. // Ref counting
  71. virtual int AddRef() = 0;
  72. virtual int Release() = 0;
  73. virtual int GetRefCount() = 0;
  74. // memory usage
  75. virtual size_t CubBytesUsed() = 0;
  76. // Serialization tools
  77. virtual bool BReadFromBuffer( const byte *pubData, int cubData ) = 0;
  78. virtual void WriteToBuffer( CUtlBuffer &memBuffer ) = 0;
  79. // memcached batching tools
  80. virtual void GetMemcachedKeyName( CUtlString &sName ) = 0;
  81. // SQL loading
  82. virtual bool BYldLoadFromSQL( CUtlVector<ISDO *> &vecSDOToLoad, CUtlVector<bool> &vecResults ) const = 0;
  83. // post-load initialization (whether loaded from SQL or memcached)
  84. virtual void PostLoadInit() = 0;
  85. // comparison function for validating memcached copies vs SQL copies
  86. virtual bool IsIdentical( ISDO *pSDO ) = 0;
  87. };
  88. //**tempcomment**typedef ISDO *(*CreateSDOFunc_t)( uint32 nAccountID );
  89. //-----------------------------------------------------------------------------
  90. // Purpose: base class for a Serialized Digital Object
  91. //-----------------------------------------------------------------------------
  92. template<typename KeyType, int eSDOType, class ProtoMsg>
  93. class CBaseSDO : public ISDO
  94. {
  95. public:
  96. typedef KeyType KeyType_t;
  97. enum { k_eType = eSDOType };
  98. CBaseSDO( const KeyType &key ) : m_Key( key ), m_nRefCount( 0 ) {}
  99. const KeyType &GetKey() const { return m_Key; }
  100. // ISDO implementation
  101. virtual int AddRef();
  102. virtual int Release();
  103. virtual int GetRefCount();
  104. virtual int GetType() const { return eSDOType; }
  105. virtual uint32 GetHashCode() const;
  106. virtual bool BReadFromBuffer( const byte *pubData, int cubData );
  107. virtual void WriteToBuffer( CUtlBuffer &memBuffer );
  108. // We use protobufs for all serialization
  109. virtual void SerializeToProtobuf( ProtoMsg &msg ) = 0;
  110. virtual bool DeserializeFromProtobuf( const ProtoMsg &msg ) = 0;
  111. // default comparison function - override to do your own compare
  112. virtual bool IsEqual( const ISDO *pSDO ) const;
  113. // default load from SQL is no-op as not all types have permanent storage - override to create a
  114. // batch load
  115. virtual bool BYldLoadFromSQL( CUtlVector<ISDO *> &vecSDOToLoad, CUtlVector<bool> &vecResults ) const;
  116. // override to do initialization after load
  117. virtual void PostLoadInit() {}
  118. // compares the serialized versions by default. Override to have more specific behavior
  119. virtual bool IsIdentical( ISDO *pSDO );
  120. // tools
  121. bool WriteToMemcached();
  122. bool DeleteFromMemcached();
  123. private:
  124. int m_nRefCount;
  125. KeyType m_Key;
  126. };
  127. //-----------------------------------------------------------------------------
  128. // Purpose: references to a database-backed object
  129. // maintains refcount of the object
  130. //-----------------------------------------------------------------------------
  131. template<class T>
  132. class CSDORef
  133. {
  134. T *m_pSDO;
  135. public:
  136. CSDORef() { m_pSDO = NULL; }
  137. explicit CSDORef( CSDORef<T> &SDORef ) { m_pSDO = SDORef.Get(); m_pSDO->AddRef(); }
  138. explicit CSDORef( T *pSDO ) { m_pSDO = pSDO; if ( m_pSDO ) m_pSDO->AddRef(); }
  139. ~CSDORef() { if ( m_pSDO ) m_pSDO->Release(); }
  140. T *Get() { return m_pSDO; }
  141. const T *Get() const { return m_pSDO; }
  142. T *operator->() { return Get(); }
  143. const T *operator->() const { return Get(); }
  144. operator const T *() const { return m_pSDO; }
  145. operator const T *() { return m_pSDO; }
  146. operator T *() { return m_pSDO; }
  147. CSDORef<T> &operator=( T *pSDO ) { if ( m_pSDO ) m_pSDO->Release(); m_pSDO = pSDO; if ( m_pSDO ) m_pSDO->AddRef(); return *this; }
  148. bool operator !() const { return Get() == NULL; }
  149. };
  150. //-----------------------------------------------------------------------------
  151. // Purpose: manages a cache of SDO objects
  152. //-----------------------------------------------------------------------------
  153. class CSDOCache
  154. {
  155. public:
  156. CSDOCache();
  157. ~CSDOCache();
  158. // Call to register SDOs. All SDO types must be registered before loaded
  159. void RegisterSDO( int nType, const char *pchName );
  160. // A struct to hold stats for the system. This is generated code in Steam. It would be great to make
  161. // it generated code here if we could bring Steam's operational stats system in the GC
  162. struct StatsSDOCache_t
  163. {
  164. uint64 m_cItemsLRUd;
  165. uint64 m_cBytesLRUd;
  166. uint64 m_cItemsUnreferenced;
  167. uint64 m_cBytesUnreferenced;
  168. uint64 m_cItemsInCache;
  169. uint64 m_cBytesInCacheEst;
  170. uint64 m_cItemsQueuedToLoad;
  171. uint64 m_cItemsLoadedFromMemcached;
  172. uint64 m_cItemsLoadedFromSQL;
  173. uint64 m_cItemsFailedLoadFromSQL;
  174. uint64 m_cQueuedMemcachedRequests;
  175. uint64 m_cQueuedSQLRequests;
  176. uint64 m_nSQLBatchSizeAvgx100;
  177. uint64 m_nMemcachedBatchSizeAvgx100;
  178. uint64 m_cSQLRequestsRejectedTooBusy;
  179. uint64 m_cMemcachedRequestsRejectedTooBusy;
  180. };
  181. // loads a SDO, and assigns a reference to it
  182. // returns false if the item couldn't be loaded, or timed out loading
  183. template<class T>
  184. bool BYldLoadSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key, bool *pbTimeoutLoading = NULL );
  185. // gets access to a SDO, but only if it's currently loaded
  186. template<class T>
  187. bool BGetLoadedSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key );
  188. // starts loading a SDO you're going to reference soon with the above BYldLoadSDO()
  189. // use this to batch up requests, hinting a set then getting reference to a set is significantly faster
  190. template<class T>
  191. void HintLoadSDO( const typename T::KeyType_t &key );
  192. // as above, but starts load a set
  193. template<class T>
  194. void HintLoadSDO( const CUtlVector<typename T::KeyType_t> &vecKeys );
  195. // force a deletes a SDO from the cache - waits until the object is not referenced
  196. template<class T>
  197. bool BYldDeleteSDO( const typename T::KeyType_t &key, uint64 unMicrosecondsToWaitForUnreferenced );
  198. // SDO refcount management
  199. void OnSDOReferenced( ISDO *pSDO );
  200. void OnSDOReleased( ISDO *pSDO );
  201. // writes a SDO to memcached immediately
  202. bool WriteSDOToMemcached( ISDO *pSDO );
  203. // delete the SDO record from memcached
  204. bool DeleteSDOFromMemcached( ISDO *pSDO );
  205. // job results
  206. void OnSDOLoadSuccess( int eSDO, int iRequestID );
  207. void OnMemcachedSDOLoadFailure( int eSDO, int iRequestID );
  208. void OnSQLSDOLoadFailure( int eSDO, int iRequestID, bool bSQLLayerSucceeded );
  209. void OnMemcachedLoadJobComplete( JobID_t jobID );
  210. void OnSQLLoadJobComplete( int eSDO, JobID_t jobID );
  211. // test access - deletes all unreferenced objects
  212. void Flush();
  213. // stats access
  214. StatsSDOCache_t &GetStats() { return m_StatsSDOCache; }
  215. int CubReferencedEst(); // number of bytes referenced in the cache
  216. // prints info about the class
  217. void Dump();
  218. // memcached verification - returns the number of mismatches
  219. //**tempcomment** void YldVerifyMemcachedData( CreateSDOFunc_t pCreateSDOFunc, CUtlVector<uint32> &vecIDs, int *pcMatches, int *pcMismatches );
  220. #ifdef DBGFLAG_VALIDATE
  221. void Validate( CValidator &validator, const char *pchName );
  222. #endif
  223. // Functions that need to be in the frame loop
  224. virtual bool BFrameFuncRunJobsUntilCompleted( CLimitTimer &limitTimer );
  225. virtual bool BFrameFuncRunMemcachedQueriesUntilCompleted( CLimitTimer &limitTimer );
  226. virtual bool BFrameFuncRunSQLQueriesOnce( CLimitTimer &limitTimer );
  227. private:
  228. // Custom comparator for our hash map
  229. class CDefPISDOEquals
  230. {
  231. public:
  232. CDefPISDOEquals() {}
  233. CDefPISDOEquals( int i ) {}
  234. inline bool operator()( const ISDO *lhs, const ISDO *rhs ) const { return ( lhs->IsEqual( rhs ) ); }
  235. inline bool operator!() const { return false; }
  236. };
  237. class CPISDOHashFunctor
  238. {
  239. public:
  240. uint32 operator()(const ISDO *pSDO ) const { return pSDO->GetHashCode(); }
  241. };
  242. template<class T>
  243. int FindLoadedSDO( const typename T::KeyType_t &key );
  244. template<class T>
  245. int QueueLoad( const typename T::KeyType_t &key );
  246. int QueueMemcachedLoad( ISDO *pSDO );
  247. // items already loaded - Maps the SDO to the LRU position
  248. CUtlHashMapLarge<ISDO *, int, CDefPISDOEquals, CPISDOHashFunctor> m_mapISDOLoaded;
  249. // items we have queued to load, in the state of either being loaded from memcached or SQL
  250. // maps SDO to a list of jobs waiting on the load
  251. CUtlHashMapLarge<ISDO *, CCopyableUtlVector<JobID_t>, CDefPISDOEquals, CPISDOHashFunctor> m_mapQueuedRequests;
  252. // requests to load from memcached
  253. CUtlLinkedList<int, int> m_queueMemcachedRequests;
  254. // Jobs currently processing memcached load requests
  255. CUtlVector<JobID_t> m_vecMemcachedJobs;
  256. // Loading from SQL is divided by SDO type
  257. struct SQLRequestManager_t
  258. {
  259. // requests to load from SQL. Maps to an ID in the map of queued requests
  260. CUtlLinkedList<int, int> m_queueRequestIDsToLoadFromSQL;
  261. // SQL jobs we have active doing reads for cache items
  262. CUtlVector<JobID_t> m_vecSQLJobs;
  263. };
  264. // a queue of requests to load from SQL for each type
  265. CUtlHashMapLarge<int, SQLRequestManager_t *> m_mapQueueSQLRequests;
  266. // jobs to wake up, since we've satisfied their SDO load request
  267. struct JobToWake_t
  268. {
  269. JobID_t m_jobID;
  270. bool m_bLoadLayerSuccess;
  271. };
  272. CUtlLinkedList<JobToWake_t, int> m_queueJobsToContinue;
  273. struct LRUItem_t
  274. {
  275. ISDO * m_pSDO;
  276. size_t m_cub;
  277. };
  278. CUtlLinkedList<LRUItem_t, int> m_listLRU;
  279. uint32 m_cubLRUItems;
  280. void RemoveSDOFromLRU( int iMapSDOLoaded );
  281. struct TypeStats_t
  282. {
  283. TypeStats_t()
  284. : m_nLoaded( 0 )
  285. , m_nRefed( 0 )
  286. , m_cubUnrefed( 0 )
  287. {}
  288. CUtlString m_strName;
  289. int m_nLoaded;
  290. int m_nRefed;
  291. int m_cubUnrefed;
  292. };
  293. StatsSDOCache_t m_StatsSDOCache;
  294. CMovingAverage<100> m_StatMemcachedBatchSize, m_StatSQLBatchSize;
  295. CUtlMap<int, TypeStats_t> m_mapTypeStats;
  296. };
  297. //-----------------------------------------------------------------------------
  298. // Definition of CBaseSDO template functions now that CSDOCache is defined and
  299. // GSDOCache() can safely be used.
  300. //-----------------------------------------------------------------------------
  301. //-----------------------------------------------------------------------------
  302. // Purpose: adds a reference
  303. //-----------------------------------------------------------------------------
  304. template<typename KeyType, int ESDOType, class ProtoMsg>
  305. int CBaseSDO<KeyType,ESDOType,ProtoMsg>::AddRef()
  306. {
  307. if ( ++m_nRefCount == 1 )
  308. GSDOCache().OnSDOReferenced( this );
  309. return m_nRefCount;
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose: releases a reference
  313. //-----------------------------------------------------------------------------
  314. template<typename KeyType, int ESDOType, class ProtoMsg>
  315. int CBaseSDO<KeyType,ESDOType,ProtoMsg>::Release()
  316. {
  317. DbgVerify( m_nRefCount > 0 );
  318. int nRefCount = --m_nRefCount;
  319. if ( nRefCount == 0 )
  320. GSDOCache().OnSDOReleased( this );
  321. return nRefCount;
  322. }
  323. //-----------------------------------------------------------------------------
  324. // Purpose: ref count
  325. //-----------------------------------------------------------------------------
  326. template<typename KeyType, int ESDOType, class ProtoMsg>
  327. int CBaseSDO<KeyType,ESDOType,ProtoMsg>::GetRefCount()
  328. {
  329. return m_nRefCount;
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Hashes the object for insertion into a hashtable
  333. //-----------------------------------------------------------------------------
  334. template<typename KeyType, int ESDOType, class ProtoMsg>
  335. uint32 CBaseSDO<KeyType,ESDOType,ProtoMsg>::GetHashCode() const
  336. {
  337. struct hashcode_t
  338. {
  339. int m_Type;
  340. KeyType_t m_Key;
  341. } hashStruct = { ESDOType, m_Key };
  342. return PearsonsHashFunctor<hashcode_t>()( hashStruct );
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose: Deserializes the object
  346. //-----------------------------------------------------------------------------
  347. template<typename KeyType, int ESDOType, class ProtoMsg>
  348. bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::BReadFromBuffer( const byte *pubData, int cubData )
  349. {
  350. ProtoMsg msg;
  351. if ( !msg.ParseFromArray( pubData, cubData ) )
  352. return false;
  353. if ( !DeserializeFromProtobuf( msg ) )
  354. return false;
  355. return true;
  356. }
  357. //-----------------------------------------------------------------------------
  358. // Purpose: Serializes the object
  359. //-----------------------------------------------------------------------------
  360. template<typename KeyType, int ESDOType, class ProtoMsg>
  361. void CBaseSDO<KeyType,ESDOType,ProtoMsg>::WriteToBuffer( CUtlBuffer &memBuffer )
  362. {
  363. ProtoMsg msg;
  364. SerializeToProtobuf( msg );
  365. CProtoBufSharedObjectHelper::AddProtoBufMessageToBuffer( memBuffer, msg, true );
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose: does an immediate write of the object to memcached
  369. //-----------------------------------------------------------------------------
  370. template<typename KeyType, int ESDOType, class ProtoMsg>
  371. bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::WriteToMemcached()
  372. {
  373. return GSDOCache().WriteSDOToMemcached( this );
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: does an immediate write of the object to memcached
  377. //-----------------------------------------------------------------------------
  378. template<typename KeyType, int ESDOType, class ProtoMsg>
  379. bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::DeleteFromMemcached()
  380. {
  381. return GSDOCache().DeleteSDOFromMemcached( this );
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose: default equality function - compares type and key
  385. //-----------------------------------------------------------------------------
  386. template<typename KeyType, int ESDOType, class ProtoMsg>
  387. bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::IsEqual( const ISDO *pSDO ) const
  388. {
  389. if ( GetType() != pSDO->GetType() )
  390. return false;
  391. return ( GetKey() == static_cast<const CBaseSDO<KeyType,ESDOType,ProtoMsg> *>( pSDO )->GetKey() );
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Purpose: Batch load a group of SDO's of the same type from SQL.
  395. // Default is no-op as not all types have permanent storage.
  396. //-----------------------------------------------------------------------------
  397. template<typename KeyType, int ESDOType, class ProtoMsg>
  398. bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::BYldLoadFromSQL( CUtlVector<ISDO *> &vecSDOToLoad, CUtlVector<bool> &vecResults ) const
  399. {
  400. FOR_EACH_VEC( vecResults, i )
  401. {
  402. vecResults[i] = true;
  403. }
  404. return true;
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: default validation function - compares serialized versions
  408. //-----------------------------------------------------------------------------
  409. bool CompareSDOObjects( ISDO *pSDO1, ISDO *pSDO2 );
  410. template<typename KeyType, int ESDOType, class ProtoMsg>
  411. bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::IsIdentical( ISDO *pSDO )
  412. {
  413. return CompareSDOObjects( this, pSDO );
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose: Finds a loaded SDO in memory. Returns the index of the object
  417. // into the loaded SDOs map
  418. //-----------------------------------------------------------------------------
  419. template<class T>
  420. int CSDOCache::FindLoadedSDO( const typename T::KeyType_t &key )
  421. {
  422. // see if we have it in cache first
  423. T probe( key );
  424. return m_mapISDOLoaded.Find( &probe );
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: Queues loading an SDO. Returns the index of the entry in the
  428. // load queue
  429. //-----------------------------------------------------------------------------
  430. template<class T>
  431. int CSDOCache::QueueLoad( const typename T::KeyType_t &key )
  432. {
  433. T probe( key );
  434. int iMap = m_mapQueuedRequests.Find( &probe );
  435. if ( m_mapQueuedRequests.IsValidIndex( iMap ) )
  436. return iMap;
  437. return QueueMemcachedLoad( new T( key ) );
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose: Preloads the object into the local cache
  441. //-----------------------------------------------------------------------------
  442. template<class T>
  443. void CSDOCache::HintLoadSDO( const typename T::KeyType_t &key )
  444. {
  445. // see if we have it in cache first
  446. if ( !m_mapISDOLoaded.IsValidIndex( FindLoadedSDO<T>( key ) ) )
  447. {
  448. QueueLoad<T>( key );
  449. }
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose: Preloads a set set of objects into the local cache
  453. //-----------------------------------------------------------------------------
  454. template<class T>
  455. void CSDOCache::HintLoadSDO( const CUtlVector<typename T::KeyType_t> &vecKeys )
  456. {
  457. FOR_EACH_VEC( vecKeys, i )
  458. {
  459. HintLoadSDO<T>( vecKeys[i] );
  460. }
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose: Returns an already-loaded SDO
  464. //-----------------------------------------------------------------------------
  465. template<class T>
  466. bool CSDOCache::BGetLoadedSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key )
  467. {
  468. int iMap = FindLoadedSDO<T>( key );
  469. if ( !m_mapISDOLoaded.IsValidIndex( iMap ) )
  470. return false;
  471. *pPSDORef = assert_cast<T*>( m_mapISDOLoaded.Key( iMap ) );
  472. return true;
  473. }
  474. //-----------------------------------------------------------------------------
  475. // Purpose: Loads the object into memory
  476. //-----------------------------------------------------------------------------
  477. template<class T>
  478. bool CSDOCache::BYldLoadSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key, bool *pbTimeoutLoading /* = NULL */ )
  479. {
  480. VPROF_BUDGET( "CSDOCache::BYldLoadSDO", VPROF_BUDGETGROUP_STEAM );
  481. if ( pbTimeoutLoading )
  482. *pbTimeoutLoading = false;
  483. // Clear the current object the ref is holding
  484. *pPSDORef = NULL;
  485. // see if we have it in cache first
  486. if ( BGetLoadedSDO( pPSDORef, key ) )
  487. return true;
  488. // otherwise batch it for load
  489. int iMap = QueueLoad<T>( key );
  490. // make sure we could queue it
  491. if ( !m_mapQueuedRequests.IsValidIndex( iMap ) )
  492. return false;
  493. // add the current job to this list waiting for the object to load
  494. m_mapQueuedRequests[iMap].AddToTail( GJobCur().GetJobID() );
  495. // wait for it to load (loader will signal our job when done)
  496. if ( !GJobCur().BYieldingWaitForWorkItem() )
  497. {
  498. if ( pbTimeoutLoading )
  499. *pbTimeoutLoading = true;
  500. return false;
  501. }
  502. // should be loaded - look up in the load map and try again
  503. bool bRet = BGetLoadedSDO( pPSDORef, key );
  504. Assert( bRet );
  505. return bRet;
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose: reloads an existing element from the SQL DB
  509. //-----------------------------------------------------------------------------
  510. template<class T>
  511. bool CSDOCache::BYldDeleteSDO( const typename T::KeyType_t &key, uint64 unMicrosecondsToWaitForUnreferenced )
  512. {
  513. // see if we have it in cache first
  514. int iMap = FindLoadedSDO<T>( key );
  515. if ( !m_mapISDOLoaded.IsValidIndex( iMap ) )
  516. {
  517. T temp( key );
  518. temp.DeleteFromMemcached();
  519. return true; /* we're good, it's not loaded */
  520. }
  521. assert_cast<T *>(m_mapISDOLoaded.Key( iMap ))->DeleteFromMemcached();
  522. // check the ref count
  523. int64 cAttempts = MAX( 1LL, (int64)(unMicrosecondsToWaitForUnreferenced / k_cMicroSecPerShellFrame) );
  524. while ( cAttempts-- > 0 )
  525. {
  526. if ( 0 == m_mapISDOLoaded.Key( iMap )->GetRefCount() )
  527. {
  528. // delete the object
  529. Assert( m_listLRU.IsValidIndex( m_mapISDOLoaded[iMap] ) );
  530. int iMapStats = m_mapTypeStats.Find( m_mapISDOLoaded.Key( iMap )->GetType() );
  531. if ( m_mapTypeStats.IsValidIndex( iMapStats ) )
  532. {
  533. m_mapTypeStats[iMapStats].m_nLoaded--;
  534. }
  535. RemoveSDOFromLRU( iMap );
  536. ISDO *pSDO = m_mapISDOLoaded.Key( iMap );
  537. m_mapISDOLoaded.RemoveAt( iMap );
  538. delete pSDO;
  539. return true;
  540. }
  541. else
  542. {
  543. GJobCur().BYieldingWaitOneFrame();
  544. }
  545. }
  546. // couldn't reload
  547. return false;
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Purpose: A class to factor out the common code in most SDO SQL loading funcitons
  551. //-----------------------------------------------------------------------------
  552. template<class T>
  553. class CSDOSQLLoadHelper
  554. {
  555. public:
  556. // Initializes with the vector of objects being loaded
  557. CSDOSQLLoadHelper( const CUtlVector<ISDO *> *vecSDOToLoad, const char *pchProfileName );
  558. // Loads all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
  559. template<class SCH>
  560. bool BYieldingExecuteSingleTable( int nFieldID, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults );
  561. // Loads the specified columns for all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
  562. template<class SCH>
  563. bool BYieldingExecuteSingleTable( int nFieldID, const CColumnSet &csetRead, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults );
  564. // Functions to load rows from more than one table at a time
  565. // Loads all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
  566. template<class SCH>
  567. void AddTableToQuery( int nFieldID );
  568. // Loads the specified columns for all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
  569. template<class SCH>
  570. void AddTableToQuery( int nFieldID, const CColumnSet &csetRead );
  571. // Executes the mutli-table query
  572. bool BYieldingExecute();
  573. // Gets the results for a table from a multi-table query
  574. template<class SCH>
  575. bool BGetResults( int nQuery, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults );
  576. private:
  577. CUtlVector<typename T::KeyType_t> m_vecKeys;
  578. CSQLAccess m_sqlAccess;
  579. struct Query_t
  580. {
  581. Query_t( const CColumnSet &columnSet, int nKeyCol ) : m_ColumnSet( columnSet ), m_nKeyCol( nKeyCol ) {}
  582. CColumnSet m_ColumnSet;
  583. int m_nKeyCol;
  584. };
  585. CUtlVector<Query_t> m_vecQueries;
  586. };
  587. //-----------------------------------------------------------------------------
  588. // Purpose: Constructor. Initializes with the vector of objects being loaded
  589. //-----------------------------------------------------------------------------
  590. template<class T>
  591. CSDOSQLLoadHelper<T>::CSDOSQLLoadHelper( const CUtlVector<ISDO *> *vecSDOToLoad, const char *pchProfileName )
  592. : m_vecKeys( 0, vecSDOToLoad->Count() )
  593. {
  594. FOR_EACH_VEC( *vecSDOToLoad, i )
  595. {
  596. m_vecKeys.AddToTail( ( (T*)vecSDOToLoad->Element( i ) )->GetKey() );
  597. }
  598. Assert( m_vecKeys.Count() > 0 );
  599. DbgVerify( m_sqlAccess.BBeginTransaction( pchProfileName ) );
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose: Loads all rows in the SCH table whose field nFieldID match the
  603. // key of an SDO being loaded.
  604. //-----------------------------------------------------------------------------
  605. template<class T>
  606. template<class SCH>
  607. bool CSDOSQLLoadHelper<T>::BYieldingExecuteSingleTable( int nFieldID, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults )
  608. {
  609. static const CColumnSet cSetRead = CColumnSet::Full<SCH>();
  610. return BYieldingExecuteSingleTable( nFieldID, cSetRead, pMapResults );
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Purpose: Loads the specified columns for all rows in the SCH table whose
  614. // field nFieldID match the key of an SDO being loaded
  615. //-----------------------------------------------------------------------------
  616. template<class T>
  617. template<class SCH>
  618. bool CSDOSQLLoadHelper<T>::BYieldingExecuteSingleTable( int nFieldID, const CColumnSet &csetRead, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults )
  619. {
  620. AddTableToQuery<SCH>( nFieldID, csetRead );
  621. if ( !BYieldingExecute() )
  622. return false;
  623. return BGetResults<SCH>( 0, pMapResults );
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Purpose: Loads all rows in the SCH table whose field nFieldID match the key
  627. // of an SDO being loaded
  628. //-----------------------------------------------------------------------------
  629. template<class T>
  630. template<class SCH>
  631. void CSDOSQLLoadHelper<T>::AddTableToQuery( int nFieldID )
  632. {
  633. static const CColumnSet cSetRead = CColumnSet::Full<SCH>();
  634. AddTableToQuery<SCH>( nFieldID, cSetRead );
  635. }
  636. //-----------------------------------------------------------------------------
  637. // Purpose: Loads the specified columns for all rows in the SCH table whose
  638. // field nFieldID match the key of an SDO being loaded
  639. //-----------------------------------------------------------------------------
  640. template<class T>
  641. template<class SCH>
  642. void CSDOSQLLoadHelper<T>::AddTableToQuery( int nFieldID, const CColumnSet &csetRead )
  643. {
  644. Assert( csetRead.GetRecordInfo() == GSchemaFull().GetSchema( SCH::k_iTable ).GetRecordInfo() );
  645. // Bind the params
  646. FOR_EACH_VEC( m_vecKeys, i )
  647. {
  648. m_sqlAccess.AddBindParam( m_vecKeys[i] );
  649. }
  650. // Build the query
  651. CFmtStr1024 sCommand;
  652. const char *pchColumnName = GSchemaFull().GetSchema( SCH::k_iTable ).GetRecordInfo()->GetColumnInfo( nFieldID ).GetName();
  653. BuildSelectStatementText( &sCommand, csetRead );
  654. sCommand.AppendFormat( " WHERE %s IN (%.*s)", pchColumnName, max( 0, ( m_vecKeys.Count() * 2 ) - 1 ), GetInsertArgString() );
  655. // Execute. Because we're in a transaction this will delay to the commit
  656. DbgVerify( m_sqlAccess.BYieldingExecute( NULL, sCommand ) );
  657. m_vecQueries.AddToTail( Query_t( csetRead, nFieldID ) );
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Purpose: Executes the mutli-table query
  661. //-----------------------------------------------------------------------------
  662. template<class T>
  663. bool CSDOSQLLoadHelper<T>::BYieldingExecute()
  664. {
  665. if ( 0 == m_vecKeys.Count() )
  666. {
  667. m_sqlAccess.RollbackTransaction();
  668. return false;
  669. }
  670. if ( !m_sqlAccess.BCommitTransaction() )
  671. return false;
  672. Assert( (uint32)m_vecQueries.Count() == m_sqlAccess.GetResultSetCount() );
  673. return (uint32)m_vecQueries.Count() == m_sqlAccess.GetResultSetCount();
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose: Gets the results for a table from a multi-table query
  677. //-----------------------------------------------------------------------------
  678. template<class T>
  679. template<class SCH>
  680. bool CSDOSQLLoadHelper<T>::BGetResults( int nQuery, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults )
  681. {
  682. pMapResults->RemoveAll();
  683. IGCSQLResultSetList *pResults = m_sqlAccess.GetResults();
  684. Assert( pResults && nQuery >= 0 && (uint32)nQuery < pResults->GetResultSetCount() && pResults->GetResultSetCount() == (uint32)m_vecQueries.Count() );
  685. if ( NULL == pResults || nQuery < 0 || (uint32)nQuery >= pResults->GetResultSetCount() || pResults->GetResultSetCount() != (uint32)m_vecQueries.Count() )
  686. return false;
  687. Assert( m_vecQueries[nQuery].m_ColumnSet.GetRecordInfo()->GetTableID() == SCH::k_iTable );
  688. if ( m_vecQueries[nQuery].m_ColumnSet.GetRecordInfo()->GetTableID() != SCH::k_iTable )
  689. return false;
  690. CUtlVector<SCH> vecResults;
  691. if ( !CopyResultToSchVector( pResults->GetResultSet( nQuery ), m_vecQueries[nQuery].m_ColumnSet, &vecResults ) )
  692. return false;
  693. // Make a map that counts how many are in each key so we can intelligently preallocate the result map
  694. // Copying around vectors of large SCHs could get expensive
  695. CUtlMap<typename T::KeyType_t, int, int > mapCounts( DefLessFunc( T::KeyType_t ) );
  696. FOR_EACH_VEC( vecResults, iVec )
  697. {
  698. uint8 *pubData;
  699. uint32 cubData;
  700. if ( !vecResults[iVec].BGetField( m_vecQueries[nQuery].m_nKeyCol, &pubData, &cubData ) )
  701. return false;
  702. Assert( cubData == sizeof( T::KeyType_t ) );
  703. if ( cubData != sizeof( T::KeyType_t ) )
  704. return false;
  705. const T::KeyType_t &key = *((T::KeyType_t *)pubData);
  706. int iMapCounts = mapCounts.Find( key );
  707. if ( mapCounts.IsValidIndex( iMapCounts ) )
  708. {
  709. mapCounts[iMapCounts]++;
  710. }
  711. else
  712. {
  713. mapCounts.Insert( key, 1 );
  714. }
  715. }
  716. // Preallocate the results map
  717. pMapResults->EnsureCapacity( mapCounts.Count() );
  718. FOR_EACH_MAP_FAST( mapCounts, iMapCount )
  719. {
  720. int iMapResult = pMapResults->Insert( mapCounts.Key( iMapCount ) );
  721. pMapResults->Element( iMapResult ).EnsureCapacity( mapCounts[iMapCount] );
  722. }
  723. FOR_EACH_VEC( vecResults, iVec )
  724. {
  725. uint8 *pubData;
  726. uint32 cubData;
  727. if ( !vecResults[iVec].BGetField( m_vecQueries[nQuery].m_nKeyCol, &pubData, &cubData ) )
  728. return false;
  729. Assert( cubData == sizeof( T::KeyType_t ) );
  730. if ( cubData != sizeof( T::KeyType_t ) )
  731. return false;
  732. const T::KeyType_t &key = *((T::KeyType_t *)pubData);
  733. int iMapResult = pMapResults->Find( key );
  734. Assert( pMapResults->IsValidIndex( iMapResult ) );
  735. if ( !pMapResults->IsValidIndex( iMapResult ) )
  736. continue;
  737. pMapResults->Element( iMapResult ).AddToTail( vecResults[iVec] );
  738. }
  739. return true;
  740. }
  741. } // namespace GCSDK
  742. #endif // SDOCACHE_H