Leaked source code of windows server 2003
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.

583 lines
13 KiB

  1. // ***************************************************************************
  2. // Copyright (C) 2000- Microsoft Corporation.
  3. // @File: vdifreeze.cpp
  4. //
  5. // PURPOSE:
  6. //
  7. // Use a coordinated VDI BACKUP WITH SNAPSHOT (SQL2000 and above)
  8. //
  9. // NOTES:
  10. // The VDI method of freeze/thaw avoids the potential resource deadlock
  11. // which prevents SQLServer from accepting a "dbcc thaw_io" when one or more
  12. // databases is frozen.
  13. //
  14. // Extern dependencies:
  15. // provision of "_Module" and the COM guids....
  16. //
  17. //
  18. // HISTORY:
  19. //
  20. // @Version: Whistler/Shiloh
  21. // 68202 11/07/00 ntsnap work
  22. //
  23. //
  24. // @EndHeader@
  25. // ***************************************************************************
  26. #if HIDE_WARNINGS
  27. #pragma warning( disable : 4786)
  28. #endif
  29. #include <stdafx.h>
  30. #include "vdierror.h"
  31. #include "vdiguid.h"
  32. ////////////////////////////////////////////////////////////////////////
  33. // Standard foo for file name aliasing. This code block must be after
  34. // all includes of VSS header files.
  35. //
  36. #ifdef VSS_FILE_ALIAS
  37. #undef VSS_FILE_ALIAS
  38. #endif
  39. #define VSS_FILE_ALIAS "SQLVFRZC"
  40. //
  41. ////////////////////////////////////////////////////////////////////////
  42. Freeze2000::Freeze2000 (
  43. const WString& serverName,
  44. ULONG maxDatabases) :
  45. m_ServerName (serverName),
  46. m_MaxDatabases (maxDatabases),
  47. m_NumDatabases (0),
  48. m_State (Unprepared),
  49. m_AbortCount (0)
  50. {
  51. CVssFunctionTracer(VSSDBG_SQLLIB, L"Freeze2000::Freeze2000");
  52. m_pDBContext = new FrozenDatabase [maxDatabases];
  53. CoCreateGuid (&m_BackupId);
  54. try
  55. {
  56. InitializeCriticalSection (&m_Latch);
  57. }
  58. catch(...)
  59. {
  60. // delete created object if we fail InitializeCriticalSection
  61. delete m_pDBContext;
  62. }
  63. }
  64. //----------------------------------------------------------
  65. // Wait for all the database threads to terminate.
  66. // This is only called by the coordinating thread while
  67. // holding exclusive access on the object.
  68. //
  69. void
  70. Freeze2000::WaitForThreads ()
  71. {
  72. CVssFunctionTracer(VSSDBG_SQLLIB, L"Freeze2000::WaitForThreads");
  73. for (int i=0; i<m_NumDatabases; i++)
  74. {
  75. FrozenDatabase* pDb = m_pDBContext+i;
  76. if (pDb->m_hThread != NULL)
  77. {
  78. DWORD status;
  79. do
  80. {
  81. status = WaitForSingleObjectEx (pDb->m_hThread, 2000, TRUE);
  82. if (m_State != Aborted && CheckAbort ())
  83. Abort ();
  84. } while (status != WAIT_OBJECT_0);
  85. CloseHandle (pDb->m_hThread);
  86. pDb->m_hThread = NULL;
  87. }
  88. }
  89. }
  90. //---------------------------------------------------------
  91. // Handle an abort.
  92. // The main thread will already hold the the lock and so
  93. // will always be successful at aborting the operation.
  94. // The database threads will attempt to abort, but won't
  95. // block in order to do so. The abort count is incremented
  96. // and the main thread is ulimately responsible for cleanup.
  97. //
  98. void
  99. Freeze2000::Abort () throw ()
  100. {
  101. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::Abort");
  102. SetAbort ();
  103. if (TryLock ())
  104. {
  105. m_State = Aborted;
  106. for (int i=0; i<m_NumDatabases; i++)
  107. {
  108. if (m_pDBContext[i].m_pIVDSet)
  109. {
  110. m_pDBContext[i].m_pIVDSet->SignalAbort ();
  111. m_pDBContext[i].m_pIVDSet->Close ();
  112. m_pDBContext[i].m_pIVDSet->Release ();
  113. m_pDBContext[i].m_pIVDSet = NULL;
  114. m_pDBContext[i].m_pIVD = NULL;
  115. }
  116. }
  117. Unlock ();
  118. }
  119. }
  120. Freeze2000::~Freeze2000 ()
  121. {
  122. Lock ();
  123. if (m_State != Complete)
  124. {
  125. // Trigger any waiting threads, cleaning up any VDI's.
  126. //
  127. Abort ();
  128. WaitForThreads ();
  129. }
  130. delete[] m_pDBContext;
  131. DeleteCriticalSection (&m_Latch);
  132. }
  133. //-------------------------------------------
  134. // Map the voids and proc call stuff to the real
  135. // thread routine.
  136. //
  137. DWORD WINAPI FreezeThreadProc(
  138. LPVOID lpParameter ) // thread data
  139. {
  140. return Freeze2000::DatabaseThreadStart (lpParameter);
  141. }
  142. DWORD Freeze2000::DatabaseThreadStart (
  143. LPVOID lpParameter ) // thread data
  144. {
  145. FrozenDatabase* pDbContext = (FrozenDatabase*)lpParameter;
  146. return pDbContext->m_pContext->DatabaseThread (pDbContext);
  147. }
  148. //-------------------------------------------
  149. // Add a database to the freeze set.
  150. //
  151. void
  152. Freeze2000::PrepareDatabase (
  153. const WString& dbName)
  154. {
  155. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Free2000::PrepareDatabase");
  156. // can't backup tempdb!
  157. //
  158. if (dbName == L"tempdb")
  159. return;
  160. Lock ();
  161. try
  162. {
  163. if (m_State == Unprepared)
  164. {
  165. m_State = Preparing;
  166. }
  167. if (m_NumDatabases >= m_MaxDatabases ||
  168. m_State != Preparing)
  169. {
  170. DBG_ASSERT(FALSE && L"Too many databases or not preparing");
  171. THROW_GENERIC;
  172. }
  173. FrozenDatabase* pDbContext = m_pDBContext+m_NumDatabases;
  174. m_NumDatabases++;
  175. pDbContext->m_pContext = this;
  176. ft.hr = CoCreateInstance (
  177. CLSID_MSSQL_ClientVirtualDeviceSet,
  178. NULL,
  179. CLSCTX_INPROC_SERVER,
  180. IID_IClientVirtualDeviceSet2,
  181. (void**)&pDbContext->m_pIVDSet);
  182. if (ft.HrFailed())
  183. {
  184. ft.LogError(VSS_ERROR_SQLLIB_CANTCREATEVDS, VSSDBG_SQLLIB << ft.hr);
  185. ft.Throw
  186. (
  187. VSSDBG_SQLLIB,
  188. ft.hr,
  189. L"Failed to create VDS object. hr = 0x%08lx",
  190. ft.hr
  191. );
  192. }
  193. VDConfig config;
  194. memset (&config, 0, sizeof(config));
  195. config.deviceCount = 1;
  196. StringFromGUID2 (m_BackupId, pDbContext->m_SetName, sizeof (pDbContext->m_SetName));
  197. swprintf (pDbContext->m_SetName+wcslen(pDbContext->m_SetName), L"%d", m_NumDatabases);
  198. // A "\" indicates a named instance, so append the name...
  199. //
  200. WCHAR* pInstance = wcschr (m_ServerName.c_str (), L'\\');
  201. if (pInstance)
  202. {
  203. pInstance++; // step over the separator
  204. }
  205. // Create the virtual device set
  206. //
  207. ft.hr = pDbContext->m_pIVDSet->CreateEx (pInstance, pDbContext->m_SetName, &config);
  208. if (ft.HrFailed())
  209. {
  210. ft.LogError(VSS_ERROR_SQLLIB_CANTCREATEVDS, VSSDBG_SQLLIB << ft.hr);
  211. ft.Throw
  212. (
  213. VSSDBG_SQLLIB,
  214. ft.hr,
  215. L"Failed to create VDS object. hr = 0x%08lx",
  216. ft.hr
  217. );
  218. }
  219. pDbContext->m_VDState = Created;
  220. pDbContext->m_DbName = dbName;
  221. pDbContext->m_hThread = CreateThread (NULL, 0,
  222. FreezeThreadProc, pDbContext, 0, NULL);
  223. if (pDbContext->m_hThread == NULL)
  224. {
  225. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  226. ft.CheckForError(VSSDBG_SQLLIB, L"CreateThread");
  227. }
  228. }
  229. catch (...)
  230. {
  231. Abort ();
  232. Unlock ();
  233. throw;
  234. }
  235. Unlock ();
  236. }
  237. //---------------------------------------------------------
  238. // Prep a database by setting up a BACKUP WITH SNAPSHOT
  239. // We perform a checkpoint first to minimize the backup checkpoint duration.
  240. // Since the backup has no way to stall for the prepare, we stall it by delaying
  241. // the VDI processing until freeze time.
  242. //
  243. DWORD
  244. Freeze2000::DatabaseThread (
  245. FrozenDatabase* pDbContext)
  246. {
  247. CVssFunctionTracer ft(VSSDBG_XML, L"Freeze2000::DatabaseThread");
  248. try
  249. {
  250. SqlConnection sql;
  251. sql.Connect (m_ServerName);
  252. WString command =
  253. L"BACKUP DATABASE [" + pDbContext->m_DbName + L"] TO VIRTUAL_DEVICE='" +
  254. pDbContext->m_SetName + L"' WITH SNAPSHOT,BUFFERCOUNT=1,BLOCKSIZE=1024";
  255. sql.SetCommand (command);
  256. sql.ExecCommand ();
  257. pDbContext->m_SuccessDetected = TRUE;
  258. }
  259. catch (...)
  260. {
  261. Abort ();
  262. }
  263. return 0;
  264. }
  265. //---------------------------------------------------------
  266. // Advance the status of each VD.
  267. // Will throw if problems are encountered.
  268. //
  269. // This routine is called in two contexts:
  270. // 1. During the "Prepare" phase, in which case 'toSnapshot' is FALSE
  271. // and the goal is to move each VD to an "open" state.
  272. // At that time, the backup metadata is not yet consumed, to the BACKUP will
  273. // be waiting (leaving the database unfrozen).
  274. // 2. During the "Freeze" phase, the metadata is consumed (and discarded),
  275. // so the BACKUP will freeze the database and send the 'VDC_Snapshot' command.
  276. //
  277. void
  278. Freeze2000::AdvanceVDState (
  279. bool toSnapshot) // TRUE when we want to advance to the snapshot open stage.
  280. {
  281. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::AdvanceVDState");
  282. // Poll over the VD's moving them to Open or SnapshotOpen
  283. //
  284. while (1)
  285. {
  286. bool didSomething = false;
  287. int nDatabasesReady = 0;
  288. for (int i=0; i<m_NumDatabases; i++)
  289. {
  290. FrozenDatabase* pDb = m_pDBContext+i;
  291. if (CheckAbort ())
  292. {
  293. THROW_GENERIC;
  294. }
  295. switch (pDb->m_VDState)
  296. {
  297. case Created:
  298. VDConfig config;
  299. ft.hr = pDb->m_pIVDSet->GetConfiguration (0, &config);
  300. if (ft.hr == VD_E_TIMEOUT)
  301. break;
  302. if (ft.HrFailed())
  303. ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDeviceSet2::GetConfiguration");
  304. ft.hr = pDb->m_pIVDSet->OpenDevice (pDb->m_SetName, &pDb->m_pIVD);
  305. if (ft.HrFailed())
  306. ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDeviceSet2::OpenDevice");
  307. pDb->m_VDState = Open;
  308. didSomething = true;
  309. // fall thru
  310. case Open:
  311. if (!toSnapshot)
  312. {
  313. nDatabasesReady++;
  314. break;
  315. }
  316. // pull commands until we see the snapshot
  317. //
  318. VDC_Command * cmd;
  319. HRESULT hr;
  320. while (pDb->m_VDState == Open &&
  321. SUCCEEDED (hr=pDb->m_pIVD->GetCommand (0, &cmd)))
  322. {
  323. DWORD completionCode;
  324. DWORD bytesTransferred;
  325. didSomething = true;
  326. switch (cmd->commandCode)
  327. {
  328. case VDC_Write:
  329. bytesTransferred = cmd->size;
  330. case VDC_Flush:
  331. completionCode = ERROR_SUCCESS;
  332. ft.hr = pDb->m_pIVD->CompleteCommand (
  333. cmd, completionCode, bytesTransferred, 0);
  334. if (ft.HrFailed())
  335. ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::CompleteCommand");
  336. break;
  337. case VDC_Snapshot:
  338. pDb->m_VDState = SnapshotOpen;
  339. pDb->m_pSnapshotCmd = cmd;
  340. break;
  341. default:
  342. ft.Trace(VSSDBG_SQLLIB, L"Unexpected VDCmd: x%x\n", cmd->commandCode);
  343. THROW_GENERIC;
  344. } // end command switch
  345. } // end command loop
  346. ft.hr = hr;
  347. if (ft.hr == VD_E_TIMEOUT)
  348. break; // no command was ready.
  349. if (ft.HrFailed())
  350. ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::GetCommand");
  351. DBG_ASSERT(pDb->m_VDState == SnapshotOpen);
  352. break;
  353. case SnapshotOpen:
  354. nDatabasesReady++;
  355. break;
  356. default:
  357. DBG_ASSERT(FALSE && L"Shouldn't get here");
  358. THROW_GENERIC;
  359. } // end switch to handle this db
  360. } // end loop over each db
  361. if (nDatabasesReady == m_NumDatabases)
  362. break;
  363. // Unless we found something to do,
  364. // delay a bit and try again.
  365. //
  366. if (didSomething)
  367. continue;
  368. SleepEx (100, TRUE);
  369. } // wait for all databases to go "Ready"
  370. }
  371. //---------------------------------------------------------
  372. // Wait for the databases to finish preparing.
  373. // This waits for the virtual devices to open up
  374. //
  375. void
  376. Freeze2000::WaitForPrepare ()
  377. {
  378. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::WaitForPrepare");
  379. Lock ();
  380. if (m_State != Preparing || CheckAbort ())
  381. {
  382. Abort ();
  383. Unlock ();
  384. THROW_GENERIC;
  385. }
  386. m_State = Prepared;
  387. try
  388. {
  389. AdvanceVDState (FALSE);
  390. }
  391. catch (...)
  392. {
  393. Abort ();
  394. Unlock ();
  395. throw;
  396. }
  397. Unlock ();
  398. }
  399. //------------------------------------------------------------------
  400. // Perform the freeze, waiting for a "Take-snapshot" from each db.
  401. //
  402. void
  403. Freeze2000::Freeze ()
  404. {
  405. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::Freeze");
  406. Lock ();
  407. if (m_State != Prepared || CheckAbort ())
  408. {
  409. Abort ();
  410. Unlock ();
  411. THROW_GENERIC;
  412. }
  413. try
  414. {
  415. m_State = Frozen;
  416. AdvanceVDState (TRUE);
  417. }
  418. catch (...)
  419. {
  420. Abort ();
  421. Unlock ();
  422. throw;
  423. }
  424. Unlock ();
  425. }
  426. //---------------------------------------------------------
  427. // Perform the thaw.
  428. //
  429. // Return TRUE if the databases were all successfully backed up
  430. // and were thawed out as expected.
  431. // FALSE is returned in any other case.
  432. // No exceptions are thrown (this routine can be used as a cleanup routine).
  433. //
  434. BOOL
  435. Freeze2000::Thaw () throw ()
  436. {
  437. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::Thaw");
  438. Lock ();
  439. if (m_State != Frozen || CheckAbort ())
  440. {
  441. Abort ();
  442. Unlock ();
  443. return FALSE;
  444. }
  445. try
  446. {
  447. // Send the "snapshot complete" messages.
  448. //
  449. int i;
  450. for (i=0; i<m_NumDatabases; i++)
  451. {
  452. FrozenDatabase* pDb = m_pDBContext+i;
  453. DBG_ASSERT (pDb->m_VDState == SnapshotOpen);
  454. ft.hr = pDb->m_pIVD->CompleteCommand (pDb->m_pSnapshotCmd, ERROR_SUCCESS, 0, 0);
  455. if (FAILED (ft.hr))
  456. ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::CompleteCommand");
  457. }
  458. // Wait for the BACKUP threads to report success.
  459. //
  460. WaitForThreads ();
  461. for (i=0; i<m_NumDatabases; i++)
  462. {
  463. FrozenDatabase* pDb = m_pDBContext+i;
  464. if (!pDb->m_SuccessDetected)
  465. {
  466. THROW_GENERIC;
  467. }
  468. }
  469. // Pull the "close" message from each VD
  470. //
  471. for (i=0; i<m_NumDatabases; i++)
  472. {
  473. FrozenDatabase* pDb = m_pDBContext+i;
  474. VDC_Command * cmd;
  475. ft.hr=pDb->m_pIVD->GetCommand (INFINITE, &cmd);
  476. if (ft.hr != VD_E_CLOSE)
  477. ft.LogError(VSS_ERROR_SQLLIB_FINALCOMMANDNOTCLOSE, VSSDBG_SQLLIB << ft.hr);
  478. pDb->m_pIVDSet->Close ();
  479. pDb->m_pIVDSet->Release ();
  480. pDb->m_pIVDSet = NULL;
  481. pDb->m_pIVD = NULL;
  482. }
  483. m_State = Complete;
  484. }
  485. catch (...)
  486. {
  487. Abort ();
  488. Unlock ();
  489. return FALSE;
  490. }
  491. Unlock ();
  492. return TRUE;
  493. }