[mapguide-commits] r5147 - in trunk/MgDev:
Common/MapGuideCommon/System Server/src/Services/Resource
Server/src/UnitTesting
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Fri Sep 17 16:23:27 EDT 2010
Author: brucedechant
Date: 2010-09-17 20:23:27 +0000 (Fri, 17 Sep 2010)
New Revision: 5147
Modified:
trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.cpp
trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.h
trunk/MgDev/Server/src/Services/Resource/LibraryRepositoryManager.cpp
trunk/MgDev/Server/src/Services/Resource/RepositoryManager.cpp
trunk/MgDev/Server/src/Services/Resource/ServerResourceService.cpp
trunk/MgDev/Server/src/UnitTesting/TestResourceService.cpp
trunk/MgDev/Server/src/UnitTesting/TestResourceService.h
Log:
Fix for trac ticket 1440 - Repository retry mechanism not being used properly
http://trac.osgeo.org/mapguide/ticket/1440
Notes:
- Updated the repository retry mechanism
Modified: trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.cpp
===================================================================
--- trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.cpp 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.cpp 2010-09-17 20:23:27 UTC (rev 5147)
@@ -345,9 +345,9 @@
const STRING MgConfigProperties::ResourceServicePropertyResourceValidationEnabled = L"ResourceValidationEnabled"; // for internal use only
const bool MgConfigProperties::DefaultResourceServicePropertyResourceValidationEnabled = true;
const STRING MgConfigProperties::ResourceServicePropertyRetryAttempts = L"RetryAttempts"; // for internal use only
-const INT32 MgConfigProperties::DefaultResourceServicePropertyRetryAttempts = 10;
+const INT32 MgConfigProperties::DefaultResourceServicePropertyRetryAttempts = 50;
const STRING MgConfigProperties::ResourceServicePropertyRetryInterval = L"RetryInterval"; // for internal use only
-const INT32 MgConfigProperties::DefaultResourceServicePropertyRetryInterval = 10;
+const INT32 MgConfigProperties::DefaultResourceServicePropertyRetryInterval = 25;
// ******************************************************************
// Site Service Properties
Modified: trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.h
===================================================================
--- trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.h 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Common/MapGuideCommon/System/ConfigProperties.h 2010-09-17 20:23:27 UTC (rev 5147)
@@ -443,11 +443,11 @@
/// Sets the number of retry attempts per operation
static const STRING ResourceServicePropertyRetryAttempts; /// value("RetryAttempts")
- static const INT32 DefaultResourceServicePropertyRetryAttempts; /// value(10)
+ static const INT32 DefaultResourceServicePropertyRetryAttempts; /// value(50)
/// Sets the time duration (in milliseconds) between retry attempts
static const STRING ResourceServicePropertyRetryInterval; /// value("RetryInterval")
- static const INT32 DefaultResourceServicePropertyRetryInterval; /// value(10)
+ static const INT32 DefaultResourceServicePropertyRetryInterval; /// value(25)
EXTERNAL_API:
Modified: trunk/MgDev/Server/src/Services/Resource/LibraryRepositoryManager.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Resource/LibraryRepositoryManager.cpp 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Server/src/Services/Resource/LibraryRepositoryManager.cpp 2010-09-17 20:23:27 UTC (rev 5147)
@@ -79,11 +79,18 @@
void MgLibraryRepositoryManager::CommitTransaction()
{
+ MG_RESOURCE_SERVICE_TRY()
+
+ // TODO: Updating the modified dates and doing the actual commit can cause DB_BUSY errors.
+ // Ensure that only the Library updates the modified dates as the session does not need them. Check that this is true?
+ // The root issue might be that 2 transactions are done here 1)Modified dates and 2)Repository Changes - can these be combined into a single transaction?
m_resourceHeaderMan->UpdateResourceModifiedDates(m_dateModifiedResources);
MgApplicationRepositoryManager::CommitTransaction();
m_resourceHeaderMan->UpdatePermissionCache();
+
+ MG_RESOURCE_SERVICE_CATCH_AND_THROW(L"MgLibraryRepositoryManager.CommitTransaction")
}
///----------------------------------------------------------------------------
Modified: trunk/MgDev/Server/src/Services/Resource/RepositoryManager.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Resource/RepositoryManager.cpp 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Server/src/Services/Resource/RepositoryManager.cpp 2010-09-17 20:23:27 UTC (rev 5147)
@@ -468,6 +468,8 @@
void MgRepositoryManager::CommitTransaction()
{
+ MG_RESOURCE_SERVICE_TRY()
+
if (NULL != m_dbTxn)
{
m_dbTxn->commit(0);
@@ -475,6 +477,8 @@
m_xmlTxn.reset();
}
+ MG_RESOURCE_SERVICE_CATCH_AND_THROW(L"MgRepositoryManager.CommitTransaction")
+
m_transacted = false;
}
Modified: trunk/MgDev/Server/src/Services/Resource/ServerResourceService.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Resource/ServerResourceService.cpp 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Server/src/Services/Resource/ServerResourceService.cpp 2010-09-17 20:23:27 UTC (rev 5147)
@@ -28,7 +28,7 @@
#include "UnmanagedDataManager.h"
#include "LogDetail.h"
-INT32 MgServerResourceService::sm_retryAttempts = 10;
+INT32 MgServerResourceService::sm_retryAttempts = 50;
ACE_Time_Value MgServerResourceService::sm_retryInterval;
MgSiteRepository* MgServerResourceService::sm_siteRepository = NULL;
@@ -42,50 +42,61 @@
///////////////////////////////////////////////////////////////////////////////
/// Server Resource Service retry macros.
-/// This pauses briefly (about 10 ms) before trying the operation again.
+/// This pauses briefly before trying the operation again.
///
#define MG_RESOURCE_SERVICE_BEGIN_OPERATION(transacted) \
- int numRetries = 0; \
+ MG_RESOURCE_SERVICE_BEGIN_RETRY() \
+ repositoryMan->Initialize(transacted); \
+
+#define MG_RESOURCE_SERVICE_END_OPERATION(maxRetries) \
+ MG_RESOURCE_SERVICE_END_RETRY(maxRetries) \
+ MG_RESOURCE_SERVICE_BEGIN_RETRY() \
+ repositoryMan->Terminate(); \
+ MG_RESOURCE_SERVICE_END_RETRY(maxRetries) \
+
+#define MG_RESOURCE_SERVICE_BEGIN_RETRY() \
+ { \
+ int numRetries = 0; \
\
- while (true) \
- { \
- try \
+ while (true) \
{ \
- repositoryMan->Initialize(transacted); \
+ try \
+ { \
-#define MG_RESOURCE_SERVICE_END_OPERATION(maxRetries) \
- break; \
- } \
- catch (MgException* e) \
- { \
- ++numRetries; \
+#define MG_RESOURCE_SERVICE_END_RETRY(maxRetries) \
+ break; \
+ } \
+ catch (MgException* e) \
+ { \
+ ++numRetries; \
\
- if ((e->IsOfClass(MapGuide_Exception_MgDbXmlException) || e->IsOfClass(MapGuide_Exception_MgDbException)) \
- && (DB_LOCK_DEADLOCK == (static_cast<MgThirdPartyException*>(e))->GetErrorCode())) \
- { \
- if (numRetries < maxRetries) \
+ if ((e->IsOfClass(MapGuide_Exception_MgDbXmlException) || e->IsOfClass(MapGuide_Exception_MgDbException)) \
+ && (DB_LOCK_DEADLOCK == (static_cast<MgThirdPartyException*>(e))->GetErrorCode())) \
{ \
- SAFE_RELEASE(e); \
+ if (numRetries < maxRetries) \
+ { \
+ SAFE_RELEASE(e); \
+ } \
+ else \
+ { \
+ throw e; \
+ } \
} \
else \
{ \
throw e; \
} \
} \
- else \
+ catch (...) \
{ \
- throw e; \
+ throw; \
} \
+ \
+ ACE_Time_Value sleepTime; \
+ sleepTime.msec((long)(sm_retryInterval.msec() + ((ACE_OS::rand()%10)+1))); \
+ ACE_OS::sleep(sleepTime); \
} \
- catch (...) \
- { \
- throw; \
- } \
- \
- ACE_OS::sleep(sm_retryInterval); \
- } \
- \
- repositoryMan->Terminate(); \
+ }
///----------------------------------------------------------------------------
/// <summary>
@@ -123,7 +134,7 @@
// Initialize performance tuning settings.
- INT32 retryInterval = 10; // in milliseconds
+ INT32 retryInterval = 25; // in milliseconds
MgConfiguration* configuration = MgConfiguration::GetInstance();
assert(NULL != configuration);
@@ -729,6 +740,7 @@
maxRetries = 0;
}
+ set<STRING> changedResources;
MG_RESOURCE_SERVICE_BEGIN_OPERATION(true)
if (NULL != content && content->IsRewindable())
@@ -743,10 +755,11 @@
repositoryMan->SetResource(resource, content, header);
+ changedResources = repositoryMan->GetChangedResources();
MG_RESOURCE_SERVICE_END_OPERATION(maxRetries)
// Update the current set of changed resources.
- UpdateChangedResources(repositoryMan->GetChangedResources());
+ UpdateChangedResources(changedResources);
MG_RESOURCE_SERVICE_CATCH_AND_THROW(L"MgServerResourceService.SetResource")
}
Modified: trunk/MgDev/Server/src/UnitTesting/TestResourceService.cpp
===================================================================
--- trunk/MgDev/Server/src/UnitTesting/TestResourceService.cpp 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Server/src/UnitTesting/TestResourceService.cpp 2010-09-17 20:23:27 UTC (rev 5147)
@@ -25,6 +25,13 @@
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TestResourceService, "TestResourceService");
+// define thread group for tiling tests
+#define THREAD_GROUP 65530
+
+#define TESTREQUESTS 500
+
+static const INT32 MG_TEST_THREADS = 8; // Adjust this to get failures!
+
const STRING adminName = L"Administrator";
const STRING adminPass = L"admin";
const STRING userLocale = L"en";
@@ -1635,3 +1642,182 @@
CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
}
}
+
+// data structure which is passed to each thread
+struct ThreadData
+{
+ INT32 threadId;
+ INT32 command;
+ bool success;
+ bool done;
+};
+
+// the method which gets executed by the ACE worker thread
+ACE_THR_FUNC_RETURN RepositoryWorker(void* param)
+{
+ // get the data for this thread
+ ThreadData* threadData = (ThreadData*)param;
+ INT32 threadId = threadData->threadId;
+ INT32 command = threadData->command;
+ ACE_DEBUG((LM_INFO, ACE_TEXT("> thread %d started\n"), threadId));
+
+ try
+ {
+ // set user info
+ Ptr<MgUserInformation> userInfo = new MgUserInformation(L"Administrator", L"admin");
+ userInfo->SetLocale(TEST_LOCALE);
+ MgUserInformation::SetCurrentUserInfo(userInfo);
+
+ // get the tile service instance
+ MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+ Ptr<MgResourceService> svcResource = dynamic_cast<MgResourceService*>(
+ serviceManager->RequestService(MgServiceType::ResourceService));
+ assert(svcResource != NULL);
+
+ switch (command)
+ {
+ case 0:
+ {
+ //Set the resource
+ Ptr<MgResourceIdentifier> resId = new MgResourceIdentifier(L"Library://UnitTests/Data/test-1.FeatureSource");
+ Ptr<MgByteSource> contentSource = new MgByteSource(resourceContentFileName);
+ Ptr<MgByteReader> contentReader = contentSource->GetReader();
+ svcResource->SetResource(resId, contentReader, NULL);
+ }
+ case 1:
+ {
+ //Set the resource data
+ Ptr<MgResourceIdentifier> resId = new MgResourceIdentifier(L"Library://UnitTests/Data/test-1.FeatureSource");
+ Ptr<MgByteSource> dataSource = new MgByteSource(dataFileName);
+ Ptr<MgByteReader> dataReader = dataSource->GetReader();
+ svcResource->SetResourceData(resId, resourceDataName, L"File", dataReader);
+ }
+ // Need to add a case that updates the session with runtime map
+ }
+
+ MgUserInformation::SetCurrentUserInfo(NULL);
+ threadData->success = true;
+ }
+ catch (MgException* e)
+ {
+ STRING message = e->GetDetails(TEST_LOCALE);
+ message += L"\n";
+ message += e->GetStackTrace(TEST_LOCALE);
+ SAFE_RELEASE(e);
+ ACE_DEBUG((LM_INFO, ACE_TEXT("RepositoryWorker - Exception:\n%W\n"), message.c_str()));
+ }
+ catch (...)
+ {
+ throw;
+ }
+
+ // clear the user info to prevent leaks - if an exception happens and this is not called it leaks about 500 bytes!
+ MgUserInformation::SetCurrentUserInfo(NULL);
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT("> thread %d done\n"), threadId));
+
+ threadData->done = true;
+ return 0;
+}
+
+void TestResourceService::TestCase_RepositoryBusy()
+{
+ // specify the number of threads to use
+ const INT32 numThreads = MG_TEST_THREADS;
+ ThreadData threadData[numThreads];
+
+ try
+ {
+ long lStart = GetTickCount();
+
+ // get the tile service instance
+ MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+ Ptr<MgResourceService> svcResource = dynamic_cast<MgResourceService*>(
+ serviceManager->RequestService(MgServiceType::ResourceService));
+ assert(svcResource != NULL);
+
+ // set user info
+ Ptr<MgUserInformation> userInfo = new MgUserInformation(L"Administrator", L"admin");
+ userInfo->SetLocale(TEST_LOCALE);
+ MgUserInformation::SetCurrentUserInfo(userInfo);
+
+ // Initialize the resource
+ Ptr<MgResourceIdentifier> resId = new MgResourceIdentifier(L"Library://UnitTests/Data/test-1.FeatureSource");
+ Ptr<MgByteSource> contentSource = new MgByteSource(resourceContentFileName);
+ Ptr<MgByteReader> contentReader = contentSource->GetReader();
+ svcResource->SetResource(resId, contentReader, NULL);
+
+ // need a thread manager
+ ACE_Thread_Manager* manager = ACE_Thread_Manager::instance();
+
+ // initialize the thread data
+ for (INT32 i=0; i<numThreads; i++)
+ {
+ threadData[i].threadId = i;
+ threadData[i].success = false;
+ threadData[i].done = true;
+ }
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nTestCase_RepositoryBusy\nThreads: %d Requests: %d\n\n"), numThreads, TESTREQUESTS));
+
+ INT32 nRequest = 0;
+ INT32 nSuccessful = 0;
+ bool bExceptionOcurred = false;
+ for (;;)
+ {
+ INT32 dc = 0;
+ for (INT32 i=0; i<numThreads; i++)
+ {
+ // check if the thread is available
+ if (threadData[i].done)
+ {
+ if(threadData[i].success)
+ nSuccessful++;
+
+ threadData[i].success = false;
+ threadData[i].done = false;
+ threadData[i].command = i%2;
+
+ // spawn a new thread using a specific group id
+ int thid = manager->spawn(ACE_THR_FUNC(RepositoryWorker), &threadData[i], 0, NULL, NULL, 0, THREAD_GROUP);
+ nRequest++;
+ }
+ }
+
+ // move on if all threads are done
+ if ((nRequest > TESTREQUESTS) || (bExceptionOcurred))
+ break;
+
+ // under Linux we get a deadlock if we don't call this every once in a while
+ if (nRequest % 25 == 0)
+ manager->wait_grp(THREAD_GROUP);
+ else
+ {
+ // pause briefly (10ms) before checking again
+ ACE_Time_Value t(0, 10000);
+ ACE_OS::sleep(t);
+ }
+ }
+
+ // make sure all threads in the group have completed
+ manager->wait_grp(THREAD_GROUP);
+
+ for (INT32 i=0; i<numThreads; i++)
+ {
+ if(threadData[i].success)
+ nSuccessful++;
+ }
+
+ ACE_DEBUG((LM_INFO, ACE_TEXT("\nRequests: %d/%d - Execution Time: = %6.4f (s)\n"), nSuccessful, nRequest, ((GetTickCount()-lStart)/1000.0)));
+ }
+ catch (MgException* e)
+ {
+ STRING message = e->GetDetails(TEST_LOCALE);
+ SAFE_RELEASE(e);
+ CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+ }
+ catch (...)
+ {
+ throw;
+ }
+}
Modified: trunk/MgDev/Server/src/UnitTesting/TestResourceService.h
===================================================================
--- trunk/MgDev/Server/src/UnitTesting/TestResourceService.h 2010-09-17 20:19:39 UTC (rev 5146)
+++ trunk/MgDev/Server/src/UnitTesting/TestResourceService.h 2010-09-17 20:23:27 UTC (rev 5147)
@@ -54,6 +54,7 @@
CPPUNIT_TEST(TestCase_DeleteResource);
CPPUNIT_TEST(TestCase_EnumerateUnmanagedData);
+ CPPUNIT_TEST(TestCase_RepositoryBusy);
CPPUNIT_TEST(TestEnd); // This must be the very last unit test
CPPUNIT_TEST_SUITE_END();
@@ -96,6 +97,7 @@
void TestCase_DeleteResource();
void TestCase_EnumerateUnmanagedData();
+ void TestCase_RepositoryBusy();
};
#endif // TESTRESOURCESERVICE_H_
More information about the mapguide-commits
mailing list