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.

5798 lines
182 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. outlog.c
  5. Abstract:
  6. Each Replica Set may have some number of outbound partners. An outbound
  7. partner can be in one of three sets at any point in time, Inactive,
  8. Active, and Eligible. The Inactive set tracks those partners that have
  9. not yet joined or have returned a failure status from a send request.
  10. The Eligible set contains those partners that can currently accept a
  11. change order. They have joined and they have not exceeded their quota
  12. of outstanding change orders. The Active set contains those partners
  13. that have joined but are not currently eligible.
  14. This module processes outbound change orders. The source can be either
  15. local change orders or inbound remote change orders. The flow for each
  16. replica set is as follows:
  17. - Accept change order from inbound log subsystem and insert it into
  18. the outbound log for the replica set.
  19. - Form the current set of "eligible" outbound partners (i.e. those that have
  20. joined and have not exceeded their outstanding Change Order Quota.
  21. - Find the joint leading change order index (JLx) over the eligible set.
  22. The Leading index for each outbound partner is the index of the next
  23. change order in the outbound log to be sent to that partner.
  24. - Starting at JLx and continuing to the current maximum change order in
  25. the outbound log (COmax) send the change order to each outbound partner
  26. (OBP) subject to the following:
  27. 1. The current change order sequence number or index (COCx) is greater
  28. than or equal to the leading change order index for this
  29. partner (COLx) (i.e. the partner has not seen this log entry).
  30. 2. The change order originator Guid, version number pair are greater
  31. than the entry in the version vector being maintained for this
  32. outbound partner. The version vector was inited when the partner
  33. joined. If not we don't need to send the partner this change order.
  34. - As the outstanding change order count for each outbound partner reaches
  35. their outstanding Change Order Quota the partner is removed from the
  36. "eligible" set.
  37. - The above loop ends when the eligible set is empty or we reach COmax
  38. in the outbound log.
  39. - Wait on either a new entry in the outbound log or a change order
  40. acknowledgement from an outbound partner and then start over, forming
  41. a new eligible set.
  42. The following diagram illustrates the relationship between COTx, COLx and COmax.
  43. It is for a 64 element Ack Vector of a typical outbound partner. The first
  44. line contains a 1 if an ack has been received. The second line shows a T and
  45. L for the Trailing and Leading index respectively (COTx and COLx). The
  46. difference between L and T is 23 change orders, 2 of which have been acked
  47. leaving 21 outstanding. The "M" is the current change order max. Change
  48. orders from L to M have not yet been sent out to this partner. The line with
  49. the caret shows the current CO being acked. Since this is at the COTx point
  50. it will advance by one. The Ack vector is a sliding window of the
  51. outstanding change orders for the partner. The "origin" is at "T". As
  52. change orders are sent out "L" is advanced but it can't catch up to "T".
  53. When "T" and "L" are the same, no change orders are still outstanding to this
  54. partner. This lets us track the Acks from the partner even when they return
  55. out of order relative to the order the change orders were sent.
  56. COTx: 215, COLx: 238, Outstanding: 21
  57. |...........................................1.1..................|
  58. |_______________________T______________________L________M________|
  59. | ^ |
  60. Assumptions/Objectives:
  61. 1. Allow batch delivery of change orders to reduce CO packet count.
  62. 2. The inbound log subsystem enforces sequence interlocks between change
  63. orders but once the change order is issued it can complete out of order
  64. relative to when it started. (sounds like a RISC machine). This is because
  65. different change orders can involve different file sizes so their fetch times
  66. can vary.
  67. 3. Multiple outbound partners can have different throughputs and schedules.
  68. 4. We can do lazy database updates of the trailing change order index number
  69. (COTx), the outbound log commit point, that we keep for each outbound
  70. partner. This allows us to reduce the frequency of database updates but it
  71. means that an outbound partner may see a change order more than once in the
  72. event of a crash. It must be prepared to discard them.
  73. 5. Each outbound partner must respond with a positive acknowledgement when it
  74. retires each change order even if it never fetches the staging file
  75. because it either rejected the changeorder or it already got it from another
  76. source (i.e. it dampened it). Failure to do this causes our ack tracker
  77. for this partner to stall and we periodically resend the oldest un-acked
  78. change order until the partner acks it or we drop the connection.
  79. Author:
  80. David A. Orbits 16-Aug-1997
  81. Environment
  82. User mode, winnt32
  83. --*/
  84. #include <ntreppch.h>
  85. #pragma hdrstop
  86. #include <frs.h>
  87. #include <tablefcn.h>
  88. #include <perrepsr.h>
  89. //
  90. // The following is the data entry format for tracking the dominant file in
  91. // the Replica OutLogDominantTable and the connection MustSendTable.
  92. //
  93. typedef struct _DOMINANT_FILE_ENTRY_ {
  94. GUID FileGuid; // File Guid (Must be at offset zero in struct)
  95. ULONGLONG TimeSent; // The time this File was last sent on the cxtion.
  96. ULONG OLSeqNum; // Outlog sequence number for change order.
  97. ULONG Flags; // Misc Flags.
  98. } DOMINANT_FILE_ENTRY, *PDOMINANT_FILE_ENTRY;
  99. //
  100. // Context to pass to DbsEnumerateOutlogTable.
  101. //
  102. typedef struct _OUTLOG_TABLE_ENUM_ {
  103. PREPLICA Replica; // Pointer to the replica structure.
  104. ULONG OldJointTrailingIndex; // Old Joint trailing index.
  105. TABLE_CTX VVTableCtx; // VV table context. Used to update OutlogVVector.
  106. } OUTLOG_TABLE_ENUM, *POUTLOG_TABLE_ENUM;
  107. #define DFT_FLAG_DELETE 0x00000001 // The OLSeqNum is for a CO that deletes the file.
  108. //
  109. // The following fields are updated when an outbound partner's OutLog process
  110. // state is saved.
  111. //
  112. ULONG OutLogUpdateFieldList[] = {CrFlagsx,
  113. CrCOLxx,
  114. CrCOTxx,
  115. CrCOTslotx,
  116. CrAckVectorx,
  117. CrCOTxNormalModeSavex};
  118. PCHAR OLReplicaProcStateNames[OL_REPLICA_PROC_MAX_STATE+1];
  119. PCHAR OLPartnerStateNames[OLP_MAX_STATE+1];
  120. FRS_QUEUE OutLogWork;
  121. BOOL NeedNewPartnerTable = TRUE;
  122. //
  123. // Outlog partner state flags.
  124. //
  125. FLAG_NAME_TABLE OlpFlagNameTable[] = {
  126. {OLP_FLAGS_ENABLED_CXTION , "EnabledCxtion " },
  127. {OLP_FLAGS_GENERATED_CXTION , "GenedCxtion " },
  128. {OLP_FLAGS_VVJOIN_MODE , "VvjoinMode " },
  129. {OLP_FLAGS_LOG_TRIMMED , "LogTrimmed " },
  130. {OLP_FLAGS_REPLAY_MODE , "ReplayMode " },
  131. {OLP_FLAGS_OPT_VVJOIN_MODE , "OptVvjoinMode " },
  132. {0, NULL}
  133. };
  134. //
  135. // If True then hold the ReplicaList lock for the duration of an outlog cleanup
  136. // cycle (which could be many minutes). If False then don't hold the lock.
  137. //
  138. BOOL LockedOutlogCleanup = FALSE;
  139. //
  140. // Maximum number of replica sets that can be created.
  141. //
  142. extern ULONG MaxNumberReplicaSets;
  143. //
  144. // The default max number of change orders outstanding.
  145. //
  146. extern ULONG MaxOutLogCoQuota;
  147. //
  148. // A CO update for a given file will not be sent out more frequently than this.
  149. //
  150. extern ULONG GOutLogRepeatInterval;
  151. //
  152. // The minimum time between checks for outbound stage file cleanup.
  153. // This cleanup pass only occurs if there has been any activity. (millisec units)
  154. //
  155. #define OUT_LOG_CLEAN_INTERVAL (60*1000)
  156. //
  157. // Max interval between forced outlog cleanup. Purpose is to age out
  158. // outlog change history. 8 hours.
  159. //
  160. #define OUT_LOG_FORCE_CLEAN_INTERVAL (8*60*60*1000)
  161. #define OUT_LOG_POLL_INTERVAL (30*1000)
  162. //
  163. // Save the partner state in the DB every OUT_LOG_SAVE_INTERVAL change orders
  164. // handled.
  165. //
  166. #define OUT_LOG_SAVE_INTERVAL 15
  167. #define OUT_LOG_TRACK_PARTNER_STATE_UPDATE(_par_, _Commit_, _Eval_) \
  168. { \
  169. PSINGLE_LIST_ENTRY SingleList; \
  170. SingleList = ((_par_)->COTx >= \
  171. ((_par_)->COTxLastSaved + OUT_LOG_SAVE_INTERVAL)) ? \
  172. (_Commit_) : (_Eval_); \
  173. PushEntryList(SingleList, &(_par_)->SaveList); \
  174. }
  175. ULONG
  176. OutLogAddReplica(
  177. IN PTHREAD_CTX ThreadCtx,
  178. IN PREPLICA Replica
  179. );
  180. ULONG
  181. OutLogRemoveReplica(
  182. IN PTHREAD_CTX ThreadCtx,
  183. IN PREPLICA Replica
  184. );
  185. ULONG
  186. OutLogInitPartner(
  187. PREPLICA Replica,
  188. PCXTION Cxtion
  189. );
  190. ULONG
  191. OutLogEnterUnjoinedPartner(
  192. PREPLICA Replica,
  193. POUT_LOG_PARTNER OutLogPartner
  194. );
  195. ULONG
  196. OutLogAddNewPartner(
  197. IN PTHREAD_CTX ThreadCtx,
  198. IN PTABLE_CTX TableCtx,
  199. IN PREPLICA Replica,
  200. IN PCXTION Cxtion
  201. );
  202. ULONG
  203. OutLogDeactivatePartner(
  204. IN PTHREAD_CTX ThreadCtx,
  205. IN PTABLE_CTX TableCtx,
  206. IN PREPLICA Replica,
  207. IN PCXTION Cxtion
  208. );
  209. ULONG
  210. OutLogActivatePartnerCmd(
  211. IN PTHREAD_CTX ThreadCtx,
  212. IN PTABLE_CTX TableCtx,
  213. IN PREPLICA Replica,
  214. IN PCXTION PartnerCxtion,
  215. IN BOOL HaveLock
  216. );
  217. ULONG
  218. OutLogActivatePartner(
  219. IN PREPLICA Replica,
  220. IN PCXTION PartnerCxtion,
  221. IN BOOL HaveLock
  222. );
  223. ULONG
  224. OutLogRemovePartner(
  225. IN PTHREAD_CTX ThreadCtx,
  226. IN PTABLE_CTX TableCtx,
  227. IN PREPLICA Replica,
  228. IN PCXTION Cxtion
  229. );
  230. ULONG
  231. OutLogReadPartner(
  232. IN PTHREAD_CTX ThreadCtx,
  233. IN PTABLE_CTX TableCtx,
  234. IN PREPLICA Replica,
  235. IN PCXTION Cxtion
  236. );
  237. ULONG
  238. OutLogClosePartner(
  239. IN PTHREAD_CTX ThreadCtx,
  240. IN PTABLE_CTX TableCtx,
  241. IN PREPLICA Replica,
  242. IN PCXTION Cxtion
  243. );
  244. ULONG
  245. OutLogProcess(
  246. PVOID FrsThreadCtxArg
  247. );
  248. ULONG
  249. OutLogProcessReplica(
  250. PTHREAD_CTX ThreadCtx,
  251. PREPLICA Replica
  252. );
  253. BOOL
  254. OutLogSendCo(
  255. PTHREAD_CTX ThreadCtx,
  256. PREPLICA Replica,
  257. POUT_LOG_PARTNER Partner,
  258. PCHANGE_ORDER_COMMAND CoCmd,
  259. ULONG JointLeadingIndex
  260. );
  261. BOOL
  262. OutLogOptimize(
  263. IN PREPLICA Replica,
  264. IN POUT_LOG_PARTNER Partner,
  265. IN PCHANGE_ORDER_COMMAND CoCmd,
  266. OUT PCHAR *SendTag
  267. );
  268. VOID
  269. OutLogSkipCo(
  270. PREPLICA Replica,
  271. ULONG JointLeadingIndex
  272. );
  273. ULONG
  274. OutLogCommitPartnerState(
  275. IN PTHREAD_CTX ThreadCtx,
  276. IN PTABLE_CTX TableCtx,
  277. IN PREPLICA Replica,
  278. IN PCXTION Cxtion
  279. );
  280. ULONG
  281. OutLogReadCo(
  282. PTHREAD_CTX ThreadCtx,
  283. PREPLICA Replica,
  284. ULONG Index
  285. );
  286. ULONG
  287. OutLogDeleteCo(
  288. PTHREAD_CTX ThreadCtx,
  289. PREPLICA Replica,
  290. ULONG Index
  291. );
  292. ULONG
  293. OutLogStartProcess(
  294. PREPLICA Replica
  295. );
  296. ULONG
  297. OutLogSubmitCo(
  298. PREPLICA Replica,
  299. PCHANGE_ORDER_ENTRY ChangeOrder
  300. );
  301. VOID
  302. OutLogAVToStr(
  303. POUT_LOG_PARTNER OutLogPartner,
  304. ULONG RetireCOx,
  305. PCHAR *OutStr1,
  306. PCHAR *OutStr2,
  307. PCHAR *OutStr3
  308. );
  309. ULONG
  310. OutLogRetireCo(
  311. PREPLICA Replica,
  312. ULONG COx,
  313. PCXTION Partner
  314. );
  315. BOOL
  316. OutLogMarkAckVector(
  317. PREPLICA Replica,
  318. ULONG COx,
  319. POUT_LOG_PARTNER OutLogPartner
  320. );
  321. ULONG
  322. OutLogSavePartnerState(
  323. IN PTHREAD_CTX ThreadCtx,
  324. IN PREPLICA Replica,
  325. IN PSINGLE_LIST_ENTRY CommitList,
  326. IN PSINGLE_LIST_ENTRY EvalList
  327. );
  328. ULONG
  329. OutLogSaveSinglePartnerState(
  330. IN PTHREAD_CTX ThreadCtx,
  331. IN PREPLICA Replica,
  332. IN PTABLE_CTX TableCtx,
  333. IN POUT_LOG_PARTNER OutLogPartner
  334. );
  335. ULONG
  336. OutLogPartnerVVJoinStart(
  337. IN PTHREAD_CTX ThreadCtx,
  338. IN PREPLICA Replica,
  339. IN POUT_LOG_PARTNER OutLogPartner
  340. );
  341. ULONG
  342. OutLogPartnerVVJoinDone(
  343. IN PTHREAD_CTX ThreadCtx,
  344. IN PREPLICA Replica,
  345. IN POUT_LOG_PARTNER OutLogPartner
  346. );
  347. ULONG
  348. OutLogCleanupLog(
  349. PTHREAD_CTX ThreadCtx,
  350. PREPLICA Replica
  351. );
  352. ULONG
  353. OutLogCleanup(
  354. PTHREAD_CTX ThreadCtx,
  355. PCOMMAND_PACKET Cmd
  356. );
  357. VOID
  358. OutLogCopyCxtionToCxtionRecord(
  359. IN PCXTION Cxtion,
  360. IN PTABLE_CTX CxtionRecord
  361. );
  362. #define OUT_LOG_DUMP_PARTNER_STATE(_sev, _olp, _cox, _desc) \
  363. FrsPrintTypeOutLogPartner(_sev, NULL, 0, _olp, _cox, _desc, DEBSUB, __LINE__)
  364. VOID
  365. FrsPrintTypeOutLogPartner(
  366. IN ULONG Severity, OPTIONAL
  367. IN PVOID Info, OPTIONAL
  368. IN DWORD Tabs, OPTIONAL
  369. IN POUT_LOG_PARTNER Olp,
  370. IN ULONG RetireCox,
  371. IN PCHAR Description,
  372. IN PCHAR Debsub, OPTIONAL
  373. IN ULONG uLineNo OPTIONAL
  374. );
  375. ULONG
  376. DbsReplicaHashCalcCoSeqNum (
  377. PVOID Buf,
  378. ULONG Length
  379. );
  380. FrsDoesCoAlterNameSpace(
  381. IN PCHANGE_ORDER_COMMAND Coc
  382. );
  383. JET_ERR
  384. DbsEnumerateOutlogTable(
  385. IN PTHREAD_CTX ThreadCtx,
  386. IN PTABLE_CTX TableCtx,
  387. IN ULONG RecordIndexLimit,
  388. IN PENUMERATE_OUTLOGTABLE_ROUTINE RecordFunction,
  389. IN PVOID Context
  390. );
  391. VOID
  392. ShutDownOutLog(
  393. VOID
  394. )
  395. /*++
  396. Routine Description:
  397. Run down the outbound log queue.
  398. Arguments:
  399. None.
  400. Return Value:
  401. None.
  402. --*/
  403. {
  404. #undef DEBSUB
  405. #define DEBSUB "ShutDownOutLog:"
  406. FrsRunDownCommand(&OutLogWork);
  407. }
  408. VOID
  409. OutLogInitialize(
  410. VOID
  411. )
  412. /*++
  413. Routine Description:
  414. Initialize the Outbound log subsystem.
  415. Arguments:
  416. None.
  417. Return Value:
  418. Frs Status
  419. --*/
  420. {
  421. #undef DEBSUB
  422. #define DEBSUB "OutLogInitialize:"
  423. LIST_ENTRY ListHead;
  424. OLReplicaProcStateNames[OL_REPLICA_INITIALIZING] = "OL_REPLICA_INITIALIZING";
  425. OLReplicaProcStateNames[OL_REPLICA_WAITING] = "OL_REPLICA_WAITING";
  426. OLReplicaProcStateNames[OL_REPLICA_WORKING] = "OL_REPLICA_WORKING";
  427. OLReplicaProcStateNames[OL_REPLICA_STOPPING] = "OL_REPLICA_STOPPING";
  428. OLReplicaProcStateNames[OL_REPLICA_STOPPED] = "OL_REPLICA_STOPPED";
  429. OLReplicaProcStateNames[OL_REPLICA_NOPARTNERS] = "OL_REPLICA_NOPARTNERS";
  430. OLReplicaProcStateNames[OL_REPLICA_ERROR] = "OL_REPLICA_ERROR";
  431. OLPartnerStateNames[OLP_INITIALIZING] = "OLP_INITIALIZING";
  432. OLPartnerStateNames[OLP_UNJOINED] = "OLP_UNJOINED";
  433. OLPartnerStateNames[OLP_ELIGIBLE] = "OLP_ELIGIBLE";
  434. OLPartnerStateNames[OLP_STANDBY] = "OLP_STANDBY";
  435. OLPartnerStateNames[OLP_AT_QUOTA] = "OLP_AT_QUOTA";
  436. OLPartnerStateNames[OLP_INACTIVE] = "OLP_INACTIVE";
  437. OLPartnerStateNames[OLP_ERROR] = "OLP_ERROR";
  438. FrsInitializeQueue(&OutLogWork, &OutLogWork);
  439. //
  440. // Create the outlog process thread.
  441. //
  442. if (!FrsIsShuttingDown &&
  443. !ThSupCreateThread(L"OutLog", NULL, OutLogProcess, ThSupExitThreadNOP)) {
  444. DPRINT(0, "ERROR - Could not create OutLogProcess thread\n");
  445. FRS_ASSERT(!"Could not create OutLogProcess thread");
  446. }
  447. }
  448. BOOL
  449. OutLogDominantKeyMatch(
  450. PVOID Buf,
  451. PVOID QKey
  452. )
  453. /*++
  454. Routine Description:
  455. Check for an exact key match.
  456. Arguments:
  457. Buf -- ptr to a Guid1.
  458. QKey -- ptr to Guid2.
  459. Return Value:
  460. TRUE if exact match.
  461. --*/
  462. {
  463. #undef DEBSUB
  464. #define DEBSUB "OutLogDominantKeyMatch:"
  465. PULONG pUL1, pUL2;
  466. pUL1 = (PULONG) Buf;
  467. pUL2 = (PULONG) QKey;
  468. if (!ValueIsMultOf4(pUL1)) {
  469. DPRINT2(0, "ERROR - Unaligned key value - addr: %08x, Data: %08x\n", pUL1, *pUL1);
  470. FRS_ASSERT(ValueIsMultOf4(pUL1));
  471. return 0xFFFFFFFF;
  472. }
  473. if (!ValueIsMultOf4(pUL2)) {
  474. DPRINT2(0, "ERROR - Unaligned key value - addr: %08x, Data: %08x\n", pUL2, *pUL2);
  475. FRS_ASSERT(ValueIsMultOf4(pUL2));
  476. return 0xFFFFFFFF;
  477. }
  478. return GUIDS_EQUAL(pUL1, pUL2);
  479. }
  480. ULONG
  481. OutLogDominantHashCalc(
  482. PVOID Buf,
  483. PULONGLONG QKey
  484. )
  485. /*++
  486. Routine Description:
  487. Calculate a hash value for the file guid used in the OutLog Dominant File Table.
  488. Arguments:
  489. Buf -- ptr to a Guid.
  490. QKey -- Returned 8 byte hash key for the QKey field of QHASH_ENTRY.
  491. Return Value:
  492. 32 bit hash value.
  493. --*/
  494. {
  495. #undef DEBSUB
  496. #define DEBSUB "OutLogDominantHashCalc:"
  497. PULONG pUL = (PULONG) Buf;
  498. PUSHORT pUS = (PUSHORT) Buf;
  499. if (!ValueIsMultOf4(pUL)) {
  500. DPRINT2(0, "ERROR - Unaligned key value - addr: %08x, Data: %08x\n", pUL, *pUL);
  501. FRS_ASSERT(ValueIsMultOf4(pUL));
  502. return 0xFFFFFFFF;
  503. }
  504. //
  505. // Calc QKey, 4 byte hash is ok.
  506. //
  507. *QKey = (ULONGLONG) (pUL[0] ^ pUL[1] ^ pUL[2] ^ pUL[3]);
  508. //
  509. // Calc hash based on the time. Include node part for remote COs.
  510. //
  511. return (ULONG) (pUS[0] ^ pUS[1] ^ pUS[2] ^ pUS[6] ^ pUS[7]);
  512. }
  513. BOOL
  514. OutLogFavorDominantFile(
  515. IN PCHANGE_ORDER_COMMAND CoCmd
  516. )
  517. /*++
  518. Routine Description:
  519. Test if this CO is a candidate for Outlog skipping. The criteria are:
  520. 1. Files only.
  521. 2. CO can't change the name space so no renames, deletes or creates.
  522. 3. CO can't be a directed co or a vvjoin co or an out of order co.
  523. 4. CO can't be an abortco, any type of refresh co, a control co, or Morphgenco.
  524. Arguments:
  525. CoCmd - ptr to CO command record.
  526. Return Value:
  527. TRUE if CO is a candidate for OutLog skipping.
  528. --*/
  529. {
  530. #undef DEBSUB
  531. #define DEBSUB "OutLogFavorDominantFile:"
  532. CHAR FlagBuffer[160];
  533. //
  534. // Certain types of COs can't be skipped.
  535. //
  536. if (FrsDoesCoAlterNameSpace(CoCmd)) {
  537. DPRINT(4, "++ noskip - alters name space\n");
  538. return FALSE;
  539. }
  540. if (CoCmdIsDirectory(CoCmd)) {
  541. DPRINT(4, "++ noskip - is directory\n");
  542. return FALSE;
  543. }
  544. if (COC_FLAG_ON(CoCmd, (CO_FLAG_ABORT_CO |
  545. CO_FLAG_GROUP_ANY_REFRESH |
  546. CO_FLAG_OUT_OF_ORDER |
  547. CO_FLAG_NEW_FILE |
  548. CO_FLAG_CONTROL |
  549. CO_FLAG_VVJOIN_TO_ORIG |
  550. CO_FLAG_MORPH_GEN |
  551. CO_FLAG_DIRECTED_CO))) {
  552. FrsFlagsToStr(CoCmd->Flags, CoFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  553. DPRINT2(4, "++ noskip - wrong CO type CoFlags: %08x [%s]\n",
  554. CoCmd->Flags, FlagBuffer);
  555. return FALSE;
  556. }
  557. return TRUE;
  558. }
  559. BOOL
  560. OutLogIsValidDominantFile(
  561. IN PCHANGE_ORDER_COMMAND CoCmd
  562. )
  563. /*++
  564. Routine Description:
  565. Test if this CO is a valid file for the OutLog dominant file table.
  566. 1. Files only.
  567. 2. CO can't be a directed co or a vvjoin co or an out of order Co.
  568. 3. CO can't be an abortco, any type of refresh co, a control co, or Morphgenco.
  569. Note: The dominant file table can contain name space changing COs since
  570. we always ship the data with the file. So we can skip a file update CO
  571. in favor of some dominant CO that may also rename or even delete the file.
  572. The latter is especially important since there is no point in shipping
  573. an update if a later CO is going to just delete the file.
  574. Note: An out of order CO is not allowed in the dominant file table since
  575. it may have reconcile data that would cause it to be rejected while the
  576. current CO would be accepted. If this becomes an important case code could
  577. be added to determine the reconciliation result between the current outlog
  578. CO and the dominant CO. I doubt this is worth it.
  579. Arguments:
  580. CoCmd - ptr to CO command record.
  581. Return Value:
  582. TRUE if CO is a candidate for OutLog Dominant file table.
  583. --*/
  584. {
  585. #undef DEBSUB
  586. #define DEBSUB "OutLogIsValidDominantFile:"
  587. CHAR FlagBuffer[160];
  588. //
  589. // Certain types of COs can't be skipped.
  590. //
  591. if (CoCmdIsDirectory(CoCmd)) {
  592. DPRINT(4, "++ not valid dominant file: directory\n");
  593. return FALSE;
  594. }
  595. if (COC_FLAG_ON(CoCmd, (CO_FLAG_ABORT_CO |
  596. CO_FLAG_GROUP_ANY_REFRESH |
  597. CO_FLAG_OUT_OF_ORDER |
  598. CO_FLAG_NEW_FILE |
  599. CO_FLAG_CONTROL |
  600. CO_FLAG_VVJOIN_TO_ORIG |
  601. CO_FLAG_MORPH_GEN |
  602. CO_FLAG_DIRECTED_CO))) {
  603. FrsFlagsToStr(CoCmd->Flags, CoFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
  604. DPRINT1(4, "++ not valid dominant file: wrong CO type CoFlags: %08x \n",
  605. CoCmd->Flags);
  606. DPRINT1(4, "++ [%s]\n", FlagBuffer);
  607. return FALSE;
  608. }
  609. return TRUE;
  610. }
  611. JET_ERR
  612. OutLogInitDominantFileTableWorker (
  613. IN PTHREAD_CTX ThreadCtx,
  614. IN PTABLE_CTX TableCtx,
  615. IN PVOID Record,
  616. IN PVOID Context
  617. )
  618. /*++
  619. Routine Description:
  620. This is a worker function passed to FrsEnumerateTable(). Each time
  621. it is called it processes a record from the Outbound log table.
  622. It scans the Outbound log table and rebuilds the Dominate File Table.
  623. Arguments:
  624. ThreadCtx - Needed to access Jet.
  625. TableCtx - A ptr to an outbound log context struct.
  626. Record - A ptr to a change order command record.
  627. Context - A ptr to the Replica struct we are working on.
  628. Thread Return Value:
  629. JET_errSuccess if enum is to continue.
  630. --*/
  631. {
  632. #undef DEBSUB
  633. #define DEBSUB "OutLogInitDominantFileTableWorker:"
  634. JET_ERR jerr;
  635. PDOMINANT_FILE_ENTRY DomFileEntry;
  636. PQHASH_ENTRY QHashEntry;
  637. PREPLICA Replica = (PREPLICA) Context;
  638. PCHANGE_ORDER_COMMAND CoCmd = (PCHANGE_ORDER_COMMAND)Record;
  639. CHAR GuidStr[GUID_CHAR_LEN];
  640. //
  641. // Ignore if entry does not meet criteria.
  642. //
  643. GuidToStr(&CoCmd->FileGuid, GuidStr);
  644. DPRINT2(4, ":S: Dominant file check of %ws %s\n", CoCmd->FileName, GuidStr);
  645. //
  646. // Certain types of COs are not candidates for skipping.
  647. //
  648. if (!OutLogIsValidDominantFile(CoCmd)) {
  649. return JET_errSuccess;
  650. }
  651. //
  652. // This is a candidate. Update the Dominant File Table.
  653. //
  654. jerr = JET_errSuccess;
  655. QHashAcquireLock(Replica->OutLogDominantTable);
  656. QHashEntry = QHashLookupLock(Replica->OutLogDominantTable, &CoCmd->FileGuid);
  657. if (QHashEntry != NULL) {
  658. //
  659. // Found a match, bump the count and record latest sequence number.
  660. //
  661. DomFileEntry = (PDOMINANT_FILE_ENTRY) (QHashEntry->Flags);
  662. QHashEntry->QData += 1;
  663. DomFileEntry->OLSeqNum = CoCmd->SequenceNumber;
  664. } else {
  665. //
  666. // Not found, insert new entry.
  667. //
  668. DomFileEntry = FrsAlloc(sizeof(DOMINANT_FILE_ENTRY));
  669. if (DomFileEntry != NULL) {
  670. DomFileEntry->Flags = 0;
  671. COPY_GUID(&DomFileEntry->FileGuid, &CoCmd->FileGuid);
  672. DomFileEntry->OLSeqNum = CoCmd->SequenceNumber;
  673. if (DOES_CO_DELETE_FILE_NAME(CoCmd)) {
  674. SetFlag(DomFileEntry->Flags, DFT_FLAG_DELETE);
  675. }
  676. QHashEntry = QHashInsertLock(Replica->OutLogDominantTable,
  677. &CoCmd->FileGuid,
  678. NULL,
  679. (ULONG_PTR) DomFileEntry);
  680. if (QHashEntry == NULL) {
  681. DPRINT2(4, "++ ERROR - Failed to insert entry into Replica OutLogDominant Table for %ws (%s)",
  682. CoCmd->FileName, GuidStr);
  683. jerr = JET_wrnNyi;
  684. }
  685. } else {
  686. jerr = JET_wrnNyi;
  687. }
  688. }
  689. QHashReleaseLock(Replica->OutLogDominantTable);
  690. return jerr;
  691. }
  692. ULONG
  693. OutLogInitDominantFileTableWorkerPart2 (
  694. PQHASH_TABLE Table,
  695. PQHASH_ENTRY BeforeNode,
  696. PQHASH_ENTRY TargetNode,
  697. PVOID Context
  698. )
  699. /*++
  700. Routine Description:
  701. This function is called thru QHashEnumerateTable() to remove entries
  702. that have no multiples.
  703. Arguments:
  704. Table - the hash table being enumerated
  705. BeforeNode -- ptr to the QhashEntry before the node of interest.
  706. TargetNode -- ptr to the QhashEntry of interest.
  707. Context - Replica ptr.
  708. Return Value:
  709. FrsError Status
  710. --*/
  711. {
  712. #undef DEBSUB
  713. #define DEBSUB "OutLogInitDominantFileTableWorkerPart2:"
  714. PDOMINANT_FILE_ENTRY DomFileEntry;
  715. if (TargetNode->QData == QUADZERO) {
  716. //DPRINT5(4, "BeforeNode: %08x, Link: %08x,"
  717. // " Flags: %08x, Tag: %08x %08x, Data: %08x %08x\n",
  718. // BeforeNode, TargetNode->NextEntry, TargetNode->Flags,
  719. // PRINTQUAD(TargetNode->QKey), PRINTQUAD(TargetNode->QData));
  720. //
  721. // Free the dominate file entry node.
  722. //
  723. DomFileEntry = (PDOMINANT_FILE_ENTRY) (TargetNode->Flags);
  724. FrsFree(DomFileEntry);
  725. TargetNode->Flags = 0;
  726. //
  727. // Tell QHashEnumerateTable() to delete the QHash node and continue the enum.
  728. //
  729. return FrsErrorDeleteRequested;
  730. }
  731. return FrsErrorSuccess;
  732. }
  733. ULONG
  734. OutLogDumpDominantFileTableWorker(
  735. PQHASH_TABLE Table,
  736. PQHASH_ENTRY BeforeNode,
  737. PQHASH_ENTRY TargetNode,
  738. PVOID Context
  739. )
  740. /*++
  741. Routine Description:
  742. Dump the OutLog Dominant File Table.
  743. Arguments:
  744. Table - the hash table being enumerated
  745. BeforeNode -- ptr to the QhashEntry before the node of interest.
  746. TargetNode -- ptr to the QhashEntry of interest.
  747. Context - Replica ptr.
  748. Return Value:
  749. FrsErrorSuccess
  750. --*/
  751. {
  752. #undef DEBSUB
  753. #define DEBSUB "OutLogDumpDominantFileTableWorker:"
  754. PDOMINANT_FILE_ENTRY DomFileEntry = (PDOMINANT_FILE_ENTRY) (TargetNode->Flags);
  755. CHAR GuidStr[GUID_CHAR_LEN];
  756. GuidToStr(&DomFileEntry->FileGuid, GuidStr);
  757. DPRINT4(4,":S: QKey: %08x %08x, Data: %08x %08x, OLSeqNum: %6d, FileGuid: %s\n",
  758. PRINTQUAD(TargetNode->QKey), PRINTQUAD(TargetNode->QData),
  759. DomFileEntry->OLSeqNum, GuidStr);
  760. return FrsErrorSuccess;
  761. }
  762. ULONG
  763. OutLogAddReplica(
  764. IN PTHREAD_CTX ThreadCtx,
  765. IN PREPLICA Replica
  766. )
  767. /*++
  768. Routine Description:
  769. Add a new replica set to the outbound log process. Called once when
  770. Replica set is created. Determine continuation index for Out Log.
  771. Init the outlog partner structs. Init the outlog table for the replica.
  772. Arguments:
  773. ThreadCtx -- A Thread context to use for dbid and sesid.
  774. Replica -- The replica set struct for the outbound log.
  775. Return Value:
  776. FrsErrorStatus
  777. --*/
  778. {
  779. #undef DEBSUB
  780. #define DEBSUB "OutLogAddReplica:"
  781. JET_ERR jerr, jerr1;
  782. ULONG FStatus;
  783. PTABLE_CTX TableCtx;
  784. PCOMMAND_PACKET CmdPkt;
  785. PVOID Key;
  786. PCXTION OutCxtion;
  787. PCHANGE_ORDER_COMMAND CoCmd;
  788. ULONG ReplicaNumber = Replica->ReplicaNumber;
  789. //
  790. // Only init once per Replica.
  791. //
  792. if (Replica->OutLogWorkState != OL_REPLICA_INITIALIZING) {
  793. return FrsErrorSuccess;
  794. }
  795. //
  796. // Allocate a table context struct to access the outbound log.
  797. //
  798. TableCtx = FrsAlloc(sizeof(TABLE_CTX));
  799. TableCtx->TableType = TABLE_TYPE_INVALID;
  800. TableCtx->Tid = JET_tableidNil;
  801. Replica->OutLogTableCtx = TableCtx;
  802. Replica->OutLogSeqNumber = 1;
  803. Replica->OutLogCOMin = 1;
  804. //
  805. // Init the table context and open the outbound log table for this replica.
  806. // Get the sequence number from the last Outbound log record.
  807. //
  808. jerr = DbsOpenTable(ThreadCtx, TableCtx, ReplicaNumber, OUTLOGTablex, NULL);
  809. if (!JET_SUCCESS(jerr)) {
  810. DPRINT1_JS(0, "DbsOpenTable (outlog) on replica number %d failed.",
  811. ReplicaNumber, jerr);
  812. DbsCloseTable(jerr1, ThreadCtx->JSesid, TableCtx);
  813. DbsFreeTableCtx(TableCtx, 1);
  814. Replica->OutLogTableCtx = FrsFree(TableCtx);
  815. return DbsTranslateJetError(jerr, FALSE);
  816. }
  817. FStatus = DbsTableMoveToRecord(ThreadCtx,
  818. TableCtx,
  819. OLSequenceNumberIndexx,
  820. FrsMoveLast);
  821. if (FRS_SUCCESS(FStatus)) {
  822. FStatus = DbsTableRead(ThreadCtx, TableCtx);
  823. if (FRS_SUCCESS(FStatus)) {
  824. CoCmd = (PCHANGE_ORDER_COMMAND) TableCtx->pDataRecord;
  825. Replica->OutLogSeqNumber = CoCmd->SequenceNumber+1;
  826. }
  827. } else {
  828. //
  829. // Outbound log is empty. Reset Seq number to 1. Zero is reserved
  830. // as the starting index for a partner that has never joined. After
  831. // the partner joins for the first time we force a VVJoin and advance
  832. // the its seq number to the end of the log.
  833. //
  834. Replica->OutLogSeqNumber = 1;
  835. }
  836. //
  837. // Everything looks good. Complete the rest of the init.
  838. //
  839. // Allocate an outlog record lock Table for the replica.
  840. //
  841. Replica->OutLogRecordLock = FrsFreeType(Replica->OutLogRecordLock);
  842. Replica->OutLogRecordLock = FrsAllocTypeSize(QHASH_TABLE_TYPE,
  843. OUTLOG_RECORD_LOCK_TABLE_SIZE);
  844. SET_QHASH_TABLE_HASH_CALC(Replica->OutLogRecordLock,
  845. DbsReplicaHashCalcCoSeqNum);
  846. //
  847. // Allocate a hash table to record the dominant file update change order
  848. // in the oubound log when multiple COs for the same file guid are present.
  849. // The hash function is on the file Guid. Then enmerate the outbound log
  850. // and build the table.
  851. //
  852. if (Replica->OutLogDominantTable == NULL) {
  853. Replica->OutLogDominantTable = FrsAllocTypeSize(QHASH_TABLE_TYPE,
  854. OUTLOG_DOMINANT_FILE_TABLE_SIZE);
  855. SET_QHASH_TABLE_FLAG(Replica->OutLogDominantTable, QHASH_FLAG_LARGE_KEY);
  856. SET_QHASH_TABLE_HASH_CALC2(Replica->OutLogDominantTable, OutLogDominantHashCalc);
  857. SET_QHASH_TABLE_KEY_MATCH(Replica->OutLogDominantTable, OutLogDominantKeyMatch);
  858. SET_QHASH_TABLE_FREE(Replica->OutLogDominantTable, FrsFree);
  859. //
  860. // Initialize the OutLogDominant Table.
  861. //
  862. if ((Replica->OutLogSeqNumber > 1) &&
  863. (Replica->OutLogRepeatInterval > 0)){
  864. jerr = FrsEnumerateTable(ThreadCtx,
  865. TableCtx,
  866. OLSequenceNumberIndexx,
  867. OutLogInitDominantFileTableWorker,
  868. Replica);
  869. if ((!JET_SUCCESS(jerr)) &&
  870. (jerr != JET_errNoCurrentRecord) &&
  871. (jerr != JET_wrnTableEmpty)) {
  872. DPRINT1_JS(0, "++ ERROR - Initializing outlog dominant table for %ws : ",
  873. Replica->ReplicaName->Name, jerr);
  874. DbsTranslateJetError(jerr, FALSE);
  875. }
  876. //
  877. // Now clear out the entries that have no multiples.
  878. //
  879. QHashEnumerateTable(Replica->OutLogDominantTable,
  880. OutLogInitDominantFileTableWorkerPart2,
  881. NULL);
  882. DPRINT1(4, ":S: Dump of outlog dominant table for %ws\n",
  883. Replica->ReplicaName->Name);
  884. QHashEnumerateTable(Replica->OutLogDominantTable,
  885. OutLogDumpDominantFileTableWorker,
  886. NULL);
  887. }
  888. }
  889. //
  890. // Close the table since we are being called from DB thread at startup.
  891. //
  892. DbsCloseTable(jerr1, ThreadCtx->JSesid, TableCtx);
  893. //
  894. // Allocate and init a command packet to initiate outbound log work on
  895. // this replica. Save the ptr to the command packet so we can reuse it
  896. // each time there is new work for this replica.
  897. //
  898. CmdPkt = FrsAllocCommand(&OutLogWork, CMD_OUTLOG_WORK_CO);
  899. FrsSetCompletionRoutine(CmdPkt, FrsCompleteKeepPkt, NULL);
  900. CmdPkt->Parameters.OutLogRequest.Replica = Replica;
  901. //
  902. // Start out with 1 outbound partner as we want COs to go to the
  903. // outbound log for future vvjoins.
  904. //
  905. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_NOPARTNERS);
  906. Replica->OutLogCxtionsJoined = 1;
  907. //
  908. // Init the out log state for each connection.
  909. // Make sure we continue at the maximum value for the OutLog Sequence Number.
  910. //
  911. Key = NULL;
  912. while (OutCxtion = GTabNextDatum(Replica->Cxtions, &Key)) {
  913. //
  914. // Ignore the (local) journal connection
  915. //
  916. if (OutCxtion->JrnlCxtion) {
  917. continue;
  918. }
  919. if (!OutCxtion->Inbound) {
  920. FRS_ASSERT(OutCxtion->OLCtx != NULL);
  921. if (OutCxtion->OLCtx->COLx > Replica->OutLogSeqNumber) {
  922. Replica->OutLogSeqNumber = OutCxtion->OLCtx->COLx;
  923. }
  924. OutLogInitPartner(Replica, OutCxtion);
  925. }
  926. }
  927. Replica->OutLogJTx = 0;
  928. Replica->OutLogJLx = Replica->OutLogSeqNumber;
  929. //
  930. // There may be old change orders in the outbound log that
  931. // weren't cleaned up because the service shut down before
  932. // the cleanup thread ran. Allow the cleanup thread to run
  933. // at least once to empty the outbound log of stale change
  934. // orders.
  935. //
  936. Replica->OutLogDoCleanup = TRUE;
  937. //
  938. // Save the cmd packet and go make an inital check for work.
  939. //
  940. OutLogAcquireLock(Replica);
  941. if (Replica->OutLogWorkState == OL_REPLICA_WAITING) {
  942. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_WORKING);
  943. }
  944. Replica->OutLogCmdPkt = CmdPkt;
  945. OutLogReleaseLock(Replica);
  946. FrsSubmitCommand(CmdPkt, FALSE);
  947. return FrsErrorSuccess;
  948. }
  949. ULONG
  950. OutLogRemoveReplica(
  951. IN PTHREAD_CTX ThreadCtx,
  952. IN PREPLICA Replica
  953. )
  954. /*++
  955. Routine Description:
  956. Remove a replica set from the outbound log process. Free memory.
  957. Even though we are stopping the Outlog process additional COs can
  958. still be inserted into the outbound log while we are shutting down.
  959. OutLogInsertCo() still needs the OutLogRecordLock hash table so it can't
  960. be released here. Instead it gets re-inited when the replica struct is
  961. reinitialized or freed.
  962. Arguments:
  963. ThreadCtx -- A Thread context to use for dbid and sesid.
  964. TableCtx
  965. Replica -- The replica set struct for the outbound log.
  966. Return Value:
  967. Frs Status
  968. --*/
  969. {
  970. #undef DEBSUB
  971. #define DEBSUB "OutLogRemoveReplica:"
  972. JET_ERR jerr;
  973. NTSTATUS Status;
  974. POUT_LOG_PARTNER Partner;
  975. PVOID Key;
  976. PCXTION OutCxtion;
  977. PTABLE_CTX TableCtx;
  978. //
  979. // Set the state to Initializing. This prevents any further calls to
  980. // the outlog process on behalf of this replica set.
  981. //
  982. OutLogAcquireLock(Replica);
  983. if (Replica->OutLogWorkState == OL_REPLICA_INITIALIZING) {
  984. //
  985. // Already removed (or never added); done
  986. //
  987. OutLogReleaseLock(Replica);
  988. return FrsErrorSuccess;
  989. }
  990. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_INITIALIZING);
  991. OutLogReleaseLock(Replica);
  992. //
  993. // Remove the outbound connections from the outbound log.
  994. //
  995. TableCtx = DbsCreateTableContext(CXTIONTablex);
  996. Key = NULL;
  997. while (OutCxtion = GTabNextDatum(Replica->Cxtions, &Key)) {
  998. //
  999. // Ignore the (local) journal connection
  1000. //
  1001. if (OutCxtion->JrnlCxtion) {
  1002. continue;
  1003. }
  1004. //
  1005. // If one of the Partner Close requests failed and took an error path
  1006. // then they may have called DbsFreeTableCtx(). Fix this here.
  1007. //
  1008. if (IS_INVALID_TABLE(TableCtx)) {
  1009. Status = DbsAllocTableCtx(CXTIONTablex, TableCtx);
  1010. if (!NT_SUCCESS(Status)) {
  1011. DPRINT_NT(0, "ERROR - DbsAllocRecordStorage failed to alloc buffers.", Status);
  1012. DbsFreeTableCtx(TableCtx, 1);
  1013. continue;
  1014. }
  1015. }
  1016. OutLogClosePartner(ThreadCtx, TableCtx, Replica, OutCxtion);
  1017. }
  1018. DbsFreeTableContext(TableCtx, 0);
  1019. //
  1020. // Close any open table and release the memory.
  1021. //
  1022. DbsCloseTable(jerr, ThreadCtx->JSesid, Replica->OutLogTableCtx);
  1023. DbsFreeTableCtx(Replica->OutLogTableCtx, 1);
  1024. Replica->OutLogTableCtx = FrsFree(Replica->OutLogTableCtx);
  1025. //
  1026. // Free the work command packet.
  1027. //
  1028. Replica->OutLogCmdPkt = FrsFreeType(Replica->OutLogCmdPkt);
  1029. //
  1030. // Free the outlog dominant QHash Table.
  1031. //
  1032. Replica->OutLogDominantTable = FrsFreeType(Replica->OutLogDominantTable);
  1033. //
  1034. // Free the remaining Outbound log partner structs.
  1035. //
  1036. FrsFreeTypeList(&Replica->OutLogEligible);
  1037. FrsFreeTypeList(&Replica->OutLogStandBy);
  1038. FrsFreeTypeList(&Replica->OutLogActive);
  1039. FrsFreeTypeList(&Replica->OutLogInActive);
  1040. return FrsErrorSuccess;
  1041. }
  1042. ULONG
  1043. OutLogInitPartner(
  1044. PREPLICA Replica,
  1045. PCXTION Cxtion
  1046. )
  1047. /*++
  1048. Routine Description:
  1049. Add a new outbound partner to the outbound log process.
  1050. Arguments:
  1051. Replica -- The replica set struct for the outbound log.
  1052. Cxtion -- The information about the partner, Guid, Send Queue, Send
  1053. Quota, ...
  1054. Return Value:
  1055. Frs Status
  1056. --*/
  1057. {
  1058. #undef DEBSUB
  1059. #define DEBSUB "OutLogInitPartner:"
  1060. //
  1061. // Not much needs to be done to enable outbound
  1062. // processing on an inbound cxtion
  1063. //
  1064. if (Cxtion->Inbound) {
  1065. return FrsErrorSuccess;
  1066. }
  1067. FRS_ASSERT(Cxtion->OLCtx);
  1068. OutLogEnterUnjoinedPartner(Replica, Cxtion->OLCtx);
  1069. return FrsErrorSuccess;
  1070. }
  1071. ULONG
  1072. OutLogEnterUnjoinedPartner(
  1073. PREPLICA Replica,
  1074. POUT_LOG_PARTNER OutLogPartner
  1075. )
  1076. /*++
  1077. Routine Description:
  1078. Put a newly inited outbound partner on the inactive list and set its
  1079. state to UNJOINED. If the Outbound Log Replica state is OL_REPLICA_NOPARTNERS
  1080. then set it to OL_REPLICA_WAITING;
  1081. Arguments:
  1082. Replica -- The replica set struct for the outbound log.
  1083. OutLogPartner -- The outbound log context for this partner.
  1084. Return Value:
  1085. Frs Status
  1086. --*/
  1087. {
  1088. #undef DEBSUB
  1089. #define DEBSUB "OutLogEnterUnjoinedPartner:"
  1090. OutLogAcquireLock(Replica);
  1091. InsertTailList(&Replica->OutLogInActive, &OutLogPartner->List);
  1092. SET_OUTLOG_PARTNER_STATE(OutLogPartner, OLP_UNJOINED);
  1093. if (Replica->OutLogWorkState == OL_REPLICA_NOPARTNERS) {
  1094. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_WAITING);
  1095. }
  1096. FRS_ASSERT(OutLogPartner->Cxtion != NULL);
  1097. //
  1098. // Track the count of outlog connections that have joined at least once.
  1099. // If this count is zero then we don't need to hold onto any staging files
  1100. // or put any change orders in the outbound log since the first connection
  1101. // to join will have to do a VVJOIN anyway.
  1102. //
  1103. if (OutLogPartner->Cxtion->LastJoinTime > (ULONGLONG) 1) {
  1104. InterlockedIncrement(&Replica->OutLogCxtionsJoined);
  1105. }
  1106. OutLogReleaseLock(Replica);
  1107. return FrsErrorSuccess;
  1108. }
  1109. ULONG
  1110. OutLogAddNewPartner(
  1111. IN PTHREAD_CTX ThreadCtx,
  1112. IN PTABLE_CTX TableCtx,
  1113. IN PREPLICA Replica,
  1114. IN PCXTION Cxtion
  1115. )
  1116. /*++
  1117. Routine Description:
  1118. Add a new outbound partner to the Replica set. Initialize the
  1119. Partner state and create an initial record in the partner table.
  1120. Arguments:
  1121. ThreadCtx -- Needed to update the database
  1122. TableCtx -- Needed to update the database
  1123. Replica -- The replica set struct for the outbound log.
  1124. Cxtion -- The information about the partner, Guid, Send Queue, Send
  1125. Quota, ...
  1126. Return Value:
  1127. Frs Status
  1128. --*/
  1129. {
  1130. #undef DEBSUB
  1131. #define DEBSUB "OutLogAddNewPartner:"
  1132. ULONG FStatus;
  1133. POUT_LOG_PARTNER OutLogPartner;
  1134. //
  1135. // Warning -- called for both inbound and outbound cxtions; be careful.
  1136. //
  1137. FRS_ASSERT(IS_CXTION_TABLE(TableCtx));
  1138. //
  1139. // Allocate an outbound log partner context, link it to the connection info.
  1140. //
  1141. if (!Cxtion->Inbound) {
  1142. OutLogPartner = FrsAllocType(OUT_LOG_PARTNER_TYPE);
  1143. Cxtion->OLCtx = OutLogPartner;
  1144. OutLogPartner->Cxtion = Cxtion;
  1145. //
  1146. // Create the initial state for this new outbound partner.
  1147. // Setting these to zero will cause the new partner to do a VVJoin
  1148. // when it first connects.
  1149. //
  1150. OutLogPartner->COLx = 0;
  1151. OutLogPartner->COTx = 0;
  1152. ResetAckVector(OutLogPartner);
  1153. OutLogPartner->OutstandingQuota = MaxOutLogCoQuota;
  1154. OutLogPartner->COTxLastSaved = OutLogPartner->COTx;
  1155. }
  1156. //
  1157. // Make last join time non-zero so if we come up against an old database
  1158. // with a zero for LastJoinTime the mismatch will cause a VVJOIN.
  1159. //
  1160. Cxtion->LastJoinTime = (ULONGLONG) 1;
  1161. //
  1162. // This is a new connection. Mark it so that we know it has not
  1163. // complete the initial sync. Also pause it so it does not start
  1164. // joining.
  1165. //
  1166. if (BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_SEEDING) && Cxtion->Inbound) {
  1167. SetCxtionFlag(Cxtion, CXTION_FLAGS_INIT_SYNC);
  1168. SetCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED);
  1169. }
  1170. //
  1171. // Update the database record in memory
  1172. //
  1173. OutLogCopyCxtionToCxtionRecord(Cxtion, TableCtx);
  1174. FStatus = DbsInsertTable(ThreadCtx, Replica, TableCtx, CXTIONTablex, NULL);
  1175. DPRINT1_FS(0, "ERROR Adding %ws\\%ws\\%ws -> %ws\\%ws",
  1176. PRINT_CXTION_PATH(Replica, Cxtion), FStatus);
  1177. if(FRS_SUCCESS(FStatus)) {
  1178. NeedNewPartnerTable = TRUE;
  1179. }
  1180. return FStatus;
  1181. }
  1182. ULONG
  1183. OutLogDeactivatePartner(
  1184. IN PTHREAD_CTX ThreadCtx,
  1185. IN PTABLE_CTX TableCtx,
  1186. IN PREPLICA Replica,
  1187. IN PCXTION Cxtion
  1188. )
  1189. /*++
  1190. Routine Description:
  1191. Put the partner in the Inactive state. Incoming ACKs can still occur but
  1192. this partner is no longer Eligible to receive change orders. Note that
  1193. Outbound Change orders still go into the log and when this partner is
  1194. again eligible to receive them the COs will be sent.
  1195. Arguments:
  1196. ThreadCtx
  1197. TableCtx
  1198. Replica -- The replica set struct for the outbound log.
  1199. Cxtion -- The information about the partner, Guid, Send Queue, Send
  1200. Quota, ...
  1201. Return Value:
  1202. Frs Status
  1203. --*/
  1204. {
  1205. #undef DEBSUB
  1206. #define DEBSUB "OutLogDeactivatePartner:"
  1207. ULONG FStatus;
  1208. POUT_LOG_PARTNER OutLogPartner = Cxtion->OLCtx;
  1209. ULONG COx;
  1210. //
  1211. // No outbound log state to deactivate for inbound cxtions
  1212. //
  1213. if (Cxtion->Inbound) {
  1214. FRS_ASSERT(OutLogPartner == NULL);
  1215. return FrsErrorSuccess;
  1216. }
  1217. //
  1218. // No need to deactivate more than once, yet.
  1219. //
  1220. if (OutLogPartner->State == OLP_INACTIVE) {
  1221. return FrsErrorSuccess;
  1222. }
  1223. OutLogAcquireLock(Replica);
  1224. SET_OUTLOG_PARTNER_INACTIVE(Replica, OutLogPartner);
  1225. OutLogReleaseLock(Replica);
  1226. OUT_LOG_DUMP_PARTNER_STATE(4, OutLogPartner, OutLogPartner->COTx, "Deactivate");
  1227. //
  1228. // Update the database with the current state of this partner.
  1229. //
  1230. FStatus = OutLogCommitPartnerState(ThreadCtx, TableCtx, Replica, Cxtion);
  1231. return FStatus;
  1232. }
  1233. ULONG
  1234. OutLogActivatePartnerCmd(
  1235. IN PTHREAD_CTX ThreadCtx,
  1236. IN PTABLE_CTX TableCtx,
  1237. IN PREPLICA Replica,
  1238. IN PCXTION Cxtion,
  1239. IN BOOL HaveLock
  1240. )
  1241. /*++
  1242. Routine Description:
  1243. Read the connection record for this outbound partner so it can resume
  1244. sending change orders where it left off when last deactivated.
  1245. Put the partner into the Eligible or Standby state. This is called
  1246. via OutLogSubmit() and the command processor so we can read the
  1247. connection record for the partner.
  1248. Arguments:
  1249. ThreadCtx -- A Thread context to use for dbid and sesid.
  1250. TableCtx -- ptr to the connection table ctx.
  1251. Replica -- The replica set struct for the outbound log.
  1252. Cxtion -- The outbound cxtion for this partner.
  1253. HaveLock -- True if the caller has the Outbound log process lock on this
  1254. replica. Otherwise we acquire it here.
  1255. Return Value:
  1256. Frs Status
  1257. --*/
  1258. {
  1259. #undef DEBSUB
  1260. #define DEBSUB "OutLogActivatePartnerCmd:"
  1261. POUT_LOG_PARTNER OutLogPartner = Cxtion->OLCtx;
  1262. ULONG FStatus;
  1263. //
  1264. // Check if this is a call to activate a partner already activated.
  1265. //
  1266. if ((OutLogPartner->State == OLP_ELIGIBLE) ||
  1267. (OutLogPartner->State == OLP_STANDBY)) {
  1268. return FrsErrorSuccess;
  1269. }
  1270. //
  1271. // If the outbound log process is active for this replica and this particular
  1272. // partner is either unjoined or inactive then get its initial state from
  1273. // its connection record.
  1274. //
  1275. if ((Replica->OutLogWorkState == OL_REPLICA_WORKING) ||
  1276. (Replica->OutLogWorkState == OL_REPLICA_WAITING)) {
  1277. if ((OutLogPartner->State == OLP_UNJOINED) ||
  1278. (OutLogPartner->State == OLP_INACTIVE)) {
  1279. FStatus = OutLogReadPartner(ThreadCtx, TableCtx, Replica, Cxtion);
  1280. if (!FRS_SUCCESS(FStatus)) {
  1281. DPRINT_FS(0, "OutLogReadPartner failed.", FStatus);
  1282. return FStatus;
  1283. }
  1284. } else {
  1285. if (OutLogPartner->State != OLP_AT_QUOTA) {
  1286. DPRINT1(1, "ERROR - Attempt to activate partner in %s state\n",
  1287. OLPartnerStateNames[OutLogPartner->State]);
  1288. return FrsErrorPartnerActivateFailed;
  1289. }
  1290. }
  1291. }
  1292. FStatus = OutLogActivatePartner(Replica, Cxtion, HaveLock);
  1293. DPRINT_FS(1, "OutLogActivatePartner failed.", FStatus);
  1294. return FStatus;
  1295. }
  1296. ULONG
  1297. OutLogCleanupMustSendTableWorker (
  1298. PQHASH_TABLE Table,
  1299. PQHASH_ENTRY BeforeNode,
  1300. PQHASH_ENTRY TargetNode,
  1301. PVOID Context
  1302. )
  1303. /*++
  1304. Routine Description:
  1305. This function is called thru QHashEnumerateTable() to remove old entries
  1306. from the partner's must-send table.
  1307. The must-send table records the outlog sequence number and the time sent
  1308. of the last change order for a given file.
  1309. If the trailing index for this partner has passed the outlog sequence number
  1310. of the entry and the Time since the CO was sent is greater than
  1311. 3 times the OutLogRepeatInterval then we conclude that we are unlikely to
  1312. see a further update to this file so the entry is removed from the table.
  1313. Arguments:
  1314. Table - the hash table being enumerated
  1315. BeforeNode -- ptr to the QhashEntry before the node of interest.
  1316. TargetNode -- ptr to the QhashEntry of interest.
  1317. Context - Replica ptr.
  1318. Return Value:
  1319. FRS status
  1320. --*/
  1321. {
  1322. #undef DEBSUB
  1323. #define DEBSUB "OutLogCleanupMustSendTableWorker:"
  1324. ULONGLONG DeltaTime;
  1325. POUT_LOG_PARTNER OutLogPartner = (POUT_LOG_PARTNER) Context;
  1326. PDOMINANT_FILE_ENTRY MustSendEntry;
  1327. MustSendEntry = (PDOMINANT_FILE_ENTRY) (TargetNode->Flags);
  1328. //
  1329. // If the outlog partner's last saved trailing Index has passed this
  1330. // entry by then delete it. Use last-saved since it changes more slowly.
  1331. //
  1332. if (MustSendEntry->OLSeqNum < OutLogPartner->COTxLastSaved) {
  1333. if (MustSendEntry->TimeSent > 0) {
  1334. //
  1335. // We have sent a CO for this File in the past.
  1336. // If we have not sent another for DeltaTime sec since
  1337. // COTx passed us by then remove the entry.
  1338. // The current value for DeltaTime is 3 times the RepeatInterval.
  1339. //
  1340. GetSystemTimeAsFileTime((PFILETIME)&DeltaTime);
  1341. DeltaTime -= MustSendEntry->TimeSent;
  1342. DeltaTime /= (ULONGLONG)(10 * 1000 * 1000);
  1343. //
  1344. // TODO: It would be better if we could avoid using the global
  1345. // value here but we don't have the ptr to the Replica struct.
  1346. // Need to pass Replica and OutLogPartner thru a temp context
  1347. // to make this work.
  1348. //
  1349. if (DeltaTime > 3 * GOutLogRepeatInterval) {
  1350. FrsFree(MustSendEntry);
  1351. TargetNode->Flags = 0;
  1352. //
  1353. // Tell QHashEnumerateTable() to delete the QHash node and continue the enum.
  1354. //
  1355. return FrsErrorDeleteRequested;
  1356. }
  1357. } else {
  1358. FrsFree(MustSendEntry);
  1359. TargetNode->Flags = 0;
  1360. return FrsErrorDeleteRequested;
  1361. }
  1362. }
  1363. return FrsErrorSuccess;
  1364. }
  1365. VOID
  1366. OutLogAssignCOLx(
  1367. IN PREPLICA Replica,
  1368. IN PCXTION Cxtion
  1369. )
  1370. /*++
  1371. Routine Description:
  1372. Check if the COs currently in outlog are enough to satisfy this connection.
  1373. If they aren't then this connection will have to go through a full idtable scan.
  1374. Here we are atempting to avoid a full idtable scan as it is expensive,
  1375. Arguments:
  1376. Replica -- The replica set struct for the outbound log.
  1377. Cxtion -- The outbound cxtion for this partner.
  1378. Return Value:
  1379. None
  1380. --*/
  1381. {
  1382. #undef DEBSUB
  1383. #define DEBSUB "OutLogAssignCOLx:"
  1384. POUT_LOG_PARTNER OutLogPartner = Cxtion->OLCtx;
  1385. PVOID Key;
  1386. PVV_ENTRY MasterVVEntry;
  1387. PVV_ENTRY OutlogVVEntry;
  1388. PVV_ENTRY CxtionVVEntry;
  1389. BOOL NeedIDTableScan;
  1390. PCOMMAND_PACKET Cmd;
  1391. LOCK_GEN_TABLE(Replica->VVector);
  1392. LOCK_GEN_TABLE(Replica->OutlogVVector);
  1393. LOCK_GEN_TABLE(Cxtion->VVector);
  1394. NeedIDTableScan = FALSE;
  1395. Key = NULL;
  1396. while (MasterVVEntry = GTabNextDatumNoLock(Replica->VVector, &Key)) {
  1397. CxtionVVEntry = GTabLookupNoLock(Cxtion->VVector, &MasterVVEntry->GVsn.Guid, NULL);
  1398. OutlogVVEntry = GTabLookupNoLock(Replica->OutlogVVector, &MasterVVEntry->GVsn.Guid, NULL);
  1399. DPRINT1(4, "MasterVVEntry->GVsn.Vsn : %08x %08x\n",
  1400. PRINTQUAD(MasterVVEntry->GVsn.Vsn));
  1401. if (OutlogVVEntry != NULL) {
  1402. DPRINT1(4, "OutlogVVEntry->GVsn.Vsn : %08x %08x\n",
  1403. PRINTQUAD(OutlogVVEntry->GVsn.Vsn));
  1404. } else {
  1405. DPRINT(4, "OutlogVVEntry->GVsn.Vsn : <null>\n");
  1406. }
  1407. if (CxtionVVEntry != NULL) {
  1408. DPRINT1(4, "CxtionVVEntry->GVsn.Vsn : %08x %08x\n",
  1409. PRINTQUAD(CxtionVVEntry->GVsn.Vsn));
  1410. } else {
  1411. DPRINT(4, "CxtionVVEntry->GVsn.Vsn : <null>\n");
  1412. }
  1413. if ((OutlogVVEntry != NULL) &&
  1414. (OutlogVVEntry->GVsn.Vsn != (ULONGLONG)1) &&
  1415. ((CxtionVVEntry == NULL) ||
  1416. (CxtionVVEntry->GVsn.Vsn < OutlogVVEntry->GVsn.Vsn))) {
  1417. NeedIDTableScan = TRUE;
  1418. }
  1419. }
  1420. if (NeedIDTableScan == TRUE) {
  1421. OutLogPartner->COLx = 0;
  1422. CXTION_STATE_TRACE(3, Cxtion, Replica, 1, "F, Needs IDTable Scan");
  1423. } else {
  1424. OutLogPartner->COLxVVJoinDone = 0;
  1425. OutLogPartner->COLxRestart = 0;
  1426. OutLogPartner->COLx = Replica->OutLogCOMin;
  1427. OutLogPartner->COTx = Replica->OutLogCOMin;
  1428. OutLogPartner->COTxNormalModeSave = 0;
  1429. //
  1430. // Reset the Ack Vector and start with a fresh count of outstanding COs.
  1431. //
  1432. ResetAckVector(OutLogPartner);
  1433. CXTION_STATE_TRACE(3, Cxtion, Replica, 1, "F, Bypassed IDTable Scan");
  1434. SetFlag(OutLogPartner->Flags, OLP_FLAGS_OPT_VVJOIN_MODE);
  1435. //
  1436. // Make sure we are not in replay mode.
  1437. //
  1438. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_REPLAY_MODE);
  1439. //
  1440. // Make sure we are not in vvjoin mode.
  1441. //
  1442. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_VVJOIN_MODE);
  1443. /*
  1444. We will not need this if we force a idtable scan for all vvjoining partners
  1445. that are in seeding state. How do we know that a vvjoining partner is in seeding
  1446. state?
  1447. Cmd = FrsAllocCommand(Replica->Queue, CMD_VVJOIN_SUCCESS);
  1448. FrsSetCompletionRoutine(Cmd, RcsCmdPktCompletionRoutine, NULL);
  1449. RsReplica(Cmd) = Replica;
  1450. RsCxtion(Cmd) = FrsDupGName(Cxtion->Name);
  1451. RsJoinGuid(Cmd) = FrsDupGuid(&Cxtion->JoinGuid);
  1452. FrsSubmitCommandServer(&ReplicaCmdServer, Cmd);
  1453. */
  1454. }
  1455. UNLOCK_GEN_TABLE(Replica->OutlogVVector);
  1456. UNLOCK_GEN_TABLE(Replica->VVector);
  1457. UNLOCK_GEN_TABLE(Cxtion->VVector);
  1458. }
  1459. ULONG
  1460. OutLogActivatePartner(
  1461. IN PREPLICA Replica,
  1462. IN PCXTION PartnerCxtion,
  1463. IN BOOL HaveLock
  1464. )
  1465. /*++
  1466. Routine Description:
  1467. Put the partner into the Eligible or Standby state. Internal call only.
  1468. Arguments:
  1469. Replica -- The replica set struct for the outbound log.
  1470. Cxtion -- The outbound cxtion for this partner.
  1471. HaveLock -- True if the caller has the Outbound log process lock on this
  1472. replica. Otherwise we acquire it here.
  1473. Return Value:
  1474. Frs Status
  1475. --*/
  1476. {
  1477. #undef DEBSUB
  1478. #define DEBSUB "OutLogActivatePartner:"
  1479. POUT_LOG_PARTNER OutLogPartner = PartnerCxtion->OLCtx;
  1480. ULONG NewState;
  1481. PLIST_ENTRY NewQueue;
  1482. BOOL Working, Waiting, AtQuota;
  1483. //
  1484. // Check if this is a call to activate a partner already activated.
  1485. //
  1486. if ((OutLogPartner->State == OLP_ELIGIBLE) ||
  1487. (OutLogPartner->State == OLP_STANDBY)) {
  1488. DPRINT(3, "ERROR - Bogus call to OutLogActivatePartner\n");
  1489. return FrsErrorSuccess;
  1490. }
  1491. //
  1492. // On the first activation of this outbound connection allocate the
  1493. // cxtion MustSend hash table to record the dominant file update change
  1494. // order in the oubound log when multiple COs for the same file guid are
  1495. // present. This is necessary to ensure we send something in the case of
  1496. // a file that is experiencing frequent updates. The hash function is on
  1497. // the file Guid.
  1498. //
  1499. if (OutLogPartner->MustSendTable == NULL) {
  1500. OutLogPartner->MustSendTable = FrsAllocTypeSize(QHASH_TABLE_TYPE,
  1501. OUTLOG_MUSTSEND_FILE_TABLE_SIZE);
  1502. SET_QHASH_TABLE_FLAG(OutLogPartner->MustSendTable, QHASH_FLAG_LARGE_KEY);
  1503. SET_QHASH_TABLE_HASH_CALC2(OutLogPartner->MustSendTable, OutLogDominantHashCalc);
  1504. SET_QHASH_TABLE_KEY_MATCH(OutLogPartner->MustSendTable, OutLogDominantKeyMatch);
  1505. SET_QHASH_TABLE_FREE(OutLogPartner->MustSendTable, FrsFree);
  1506. } else {
  1507. //
  1508. // Each time we activate an outlog partner we scan its MustSendTable
  1509. // for expired entries. Change orders created for VVJoins do not
  1510. // get inserted into the MustSendTable so skip the cleanup.
  1511. // Note: may need something smarter here if this gets expensive.
  1512. //
  1513. if (!InVVJoinMode(OutLogPartner)) {
  1514. QHashEnumerateTable(OutLogPartner->MustSendTable,
  1515. OutLogCleanupMustSendTableWorker,
  1516. OutLogPartner);
  1517. }
  1518. }
  1519. if (!HaveLock) {OutLogAcquireLock(Replica);}
  1520. //
  1521. // If VVJOIN required then set the leading index to zero to force it.
  1522. //
  1523. if (CxtionFlagIs(PartnerCxtion, CXTION_FLAGS_PERFORM_VVJOIN) || OutLogPartner->COLx == 0) {
  1524. //
  1525. // This is either a new connection or it is a connection that we decided to
  1526. // disable so that we could cleanup the outlog. Check if the COs currently in
  1527. // outlog are enough to satisfy this connection. If they aren't then this
  1528. // connection will have to go through a full idtable scan. Here we are
  1529. // atempting to avoid a full idtable scan as it is expensive,
  1530. //
  1531. OutLogAssignCOLx(Replica, PartnerCxtion);
  1532. ClearCxtionFlag(PartnerCxtion, CXTION_FLAGS_PERFORM_VVJOIN);
  1533. }
  1534. //
  1535. // Ditto if we had to trim the outlog of change orders that never
  1536. // got sent to this cxtion.
  1537. //
  1538. if (BooleanFlagOn(OutLogPartner->Flags, OLP_FLAGS_LOG_TRIMMED)) {
  1539. OutLogPartner->COLx = 0;
  1540. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_LOG_TRIMMED);
  1541. }
  1542. FrsRemoveEntryList(&OutLogPartner->List);
  1543. //
  1544. // If the Outbound log process is not running for this replica,
  1545. // stick the partner struct on the InActive list.
  1546. // Note: we could still see more ACKs come in for this partner.
  1547. //
  1548. NewQueue = &Replica->OutLogInActive;
  1549. NewState = OLP_INACTIVE;
  1550. //
  1551. // If the replica is in the working state, put the partner struct
  1552. // on the standby list (if it's not at the quota limit). The outbound
  1553. // log process will pick it up when it first starts but if it is
  1554. // already started it will wait until the next cycle.
  1555. //
  1556. // If Replica is waiting for outbound log work put the partner
  1557. // struct on the Eligible list, set the Replica outbound log
  1558. // state to working and insert the command packet on the queue.
  1559. //
  1560. Working = (Replica->OutLogWorkState == OL_REPLICA_WORKING);
  1561. Waiting = (Replica->OutLogWorkState == OL_REPLICA_WAITING);
  1562. AtQuota = (OutLogPartner->OutstandingCos >= OutLogPartner->OutstandingQuota);
  1563. if (Working || Waiting) {
  1564. if (AtQuota) {
  1565. //
  1566. // Activating a partner that is still at max quota for COs
  1567. // outstanding. Put it on the active list but it won't go to the
  1568. // eligible list until some Acks come back.
  1569. //
  1570. NewQueue = &Replica->OutLogActive;
  1571. NewState = OLP_AT_QUOTA;
  1572. DPRINT3(1, "STILL_AT_QUOTA on OutLog partner %08x on Replica %08x, %ws\n",
  1573. OutLogPartner, Replica, Replica->ReplicaName->Name);
  1574. FRS_PRINT_TYPE(0, OutLogPartner);
  1575. } else {
  1576. NewQueue = (Working ? &Replica->OutLogStandBy : &Replica->OutLogEligible);
  1577. NewState = (Working ? OLP_STANDBY : OLP_ELIGIBLE);
  1578. }
  1579. }
  1580. SET_OUTLOG_PARTNER_STATE(OutLogPartner, NewState);
  1581. InsertTailList(NewQueue, &OutLogPartner->List);
  1582. //
  1583. // If Replica is waiting for outbound log work set the Replica outbound
  1584. // log state to working and insert the command packet on the queue.
  1585. //
  1586. if (Waiting && !AtQuota) {
  1587. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_WORKING);
  1588. FrsSubmitCommand(Replica->OutLogCmdPkt, FALSE);
  1589. }
  1590. if (!HaveLock) {OutLogReleaseLock(Replica);}
  1591. return FrsErrorSuccess;
  1592. }
  1593. ULONG
  1594. OutLogClosePartner(
  1595. IN PTHREAD_CTX ThreadCtx,
  1596. IN PTABLE_CTX TableCtx,
  1597. IN PREPLICA Replica,
  1598. IN PCXTION Cxtion
  1599. )
  1600. /*++
  1601. Routine Description:
  1602. Close the partner, saving its state, and Remove the partner from the
  1603. Outbound log process. Free the context.
  1604. Arguments:
  1605. ThreadCtx
  1606. TableCtx
  1607. Replica -- The replica set struct for the outbound log.
  1608. Cxtion -- The information about the partner, Guid, Send Queue, Send
  1609. Quota, ...
  1610. Return Value:
  1611. Frs Status
  1612. --*/
  1613. {
  1614. #undef DEBSUB
  1615. #define DEBSUB "OutLogClosePartner:"
  1616. POUT_LOG_PARTNER OutLogPartner = Cxtion->OLCtx;
  1617. ULONG COx;
  1618. //
  1619. // Not open or an inbound cxtion; done
  1620. //
  1621. if (OutLogPartner == NULL) {
  1622. return FrsErrorSuccess;
  1623. }
  1624. OutLogAcquireLock(Replica);
  1625. FrsRemoveEntryList(&OutLogPartner->List);
  1626. SET_OUTLOG_PARTNER_STATE(OutLogPartner, OLP_INITIALIZING);
  1627. OutLogReleaseLock(Replica);
  1628. OUT_LOG_DUMP_PARTNER_STATE(4, OutLogPartner, OutLogPartner->COTx, "Close partner");
  1629. //
  1630. // Update the database with the current state of this partner.
  1631. //
  1632. return OutLogCommitPartnerState(ThreadCtx, TableCtx, Replica, Cxtion);
  1633. }
  1634. ULONG
  1635. OutLogRemovePartner(
  1636. IN PTHREAD_CTX ThreadCtx,
  1637. IN PTABLE_CTX TableCtx,
  1638. IN PREPLICA Replica,
  1639. IN PCXTION ArgCxtion
  1640. )
  1641. /*++
  1642. Routine Description:
  1643. This partner is being removed from the database.
  1644. Remove the partner from the Outbound log process. If this is the last
  1645. outbound partner for this replica set then empty the outbound log and
  1646. set the outbound replica state to OL_REPLICA_NOPARTNERS.
  1647. Assumes:
  1648. The connection is already removed from the connection table so we
  1649. enumerate the table to see if any more oubound partners exist.
  1650. Arguments:
  1651. ThreadCtx
  1652. TableCtx
  1653. Replica -- The replica set struct for the outbound log.
  1654. ArgCxtion -- The information about the partner, Guid, Send Queue, Send
  1655. Quota, ...
  1656. Return Value:
  1657. Frs Status
  1658. --*/
  1659. {
  1660. #undef DEBSUB
  1661. #define DEBSUB "OutLogRemovePartner:"
  1662. PVOID Key;
  1663. PCXTION_RECORD CxtionRecord = TableCtx->pDataRecord;
  1664. ULONG FStatus = FrsErrorSuccess;
  1665. PCXTION Cxtion;
  1666. FRS_ASSERT(IS_CXTION_TABLE(TableCtx));
  1667. //
  1668. // Copy the fields from the cxtion into the table's cxtion record
  1669. //
  1670. OutLogCopyCxtionToCxtionRecord(ArgCxtion, TableCtx);
  1671. //
  1672. // Seek to the CxtionTable record and delete it.
  1673. //
  1674. FStatus = DbsDeleteTableRecordByIndex(ThreadCtx,
  1675. Replica,
  1676. TableCtx,
  1677. &CxtionRecord->CxtionGuid,
  1678. CrCxtionGuidxIndexx,
  1679. CXTIONTablex);
  1680. if (!FRS_SUCCESS(FStatus)) {
  1681. DPRINT1_FS(0, "ERROR Deleting %ws\\%ws\\%ws -> %ws\\%ws",
  1682. PRINT_CXTION_PATH(Replica, ArgCxtion), FStatus);
  1683. return FStatus;
  1684. }
  1685. NeedNewPartnerTable = TRUE;
  1686. //
  1687. // Inbound partner; done
  1688. //
  1689. if (ArgCxtion->Inbound) {
  1690. return FStatus;
  1691. }
  1692. FRS_ASSERT(ArgCxtion->OLCtx);
  1693. //
  1694. // An embedded closepartner w/o the state update
  1695. //
  1696. OutLogAcquireLock(Replica);
  1697. FrsRemoveEntryList(&ArgCxtion->OLCtx->List);
  1698. SET_OUTLOG_PARTNER_STATE(ArgCxtion->OLCtx, OLP_INITIALIZING);
  1699. OutLogReleaseLock(Replica);
  1700. //
  1701. // There may be old change orders in the outbound log that
  1702. // weren't cleaned up because the service shut down before
  1703. // the cleanup thread ran. Allow the cleanup thread to run
  1704. // at least once to empty the outbound log of stale change
  1705. // orders.
  1706. //
  1707. Replica->OutLogDoCleanup = TRUE;
  1708. //
  1709. // See if any outbound partners remain.
  1710. //
  1711. LOCK_CXTION_TABLE(Replica);
  1712. Key = NULL;
  1713. while (Cxtion = GTabNextDatumNoLock(Replica->Cxtions, &Key)) {
  1714. if (!Cxtion->Inbound &&
  1715. !GUIDS_EQUAL(ArgCxtion->Name->Guid, Cxtion->Name->Guid)) {
  1716. UNLOCK_CXTION_TABLE(Replica);
  1717. return FStatus;
  1718. }
  1719. }
  1720. UNLOCK_CXTION_TABLE(Replica);
  1721. //
  1722. // No outbound connections left.
  1723. //
  1724. OutLogAcquireLock(Replica);
  1725. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_NOPARTNERS);
  1726. //
  1727. // Set outbound partners to 1 so that COs go to the
  1728. // outbound log for future vvjoins.
  1729. //
  1730. Replica->OutLogCxtionsJoined = 1;
  1731. OutLogReleaseLock(Replica);
  1732. return FStatus;
  1733. }
  1734. ULONG
  1735. OutLogReadPartner(
  1736. IN PTHREAD_CTX ThreadCtx,
  1737. IN PTABLE_CTX TableCtx,
  1738. IN PREPLICA Replica,
  1739. IN PCXTION Cxtion
  1740. )
  1741. /*++
  1742. Routine Description:
  1743. Read the outlog partner state from the connection record so we can
  1744. reactivate the partner.
  1745. Arguments:
  1746. ThreadCtx
  1747. TableCtx
  1748. Replica -- The replica set struct for the outbound log.
  1749. Cxtion -- The information about the partner, Guid, Send Queue, Send
  1750. Quota, ...
  1751. Return Value:
  1752. Frs Status
  1753. --*/
  1754. {
  1755. #undef DEBSUB
  1756. #define DEBSUB "OutLogReadPartner:"
  1757. PCXTION_RECORD CxtionRecord = TableCtx->pDataRecord;
  1758. ULONG FStatus = FrsErrorSuccess;
  1759. POUT_LOG_PARTNER OutLogPartner;
  1760. ULONG COx;
  1761. FRS_ASSERT(IS_CXTION_TABLE(TableCtx));
  1762. //
  1763. // Open the connection table for the replica set and read the connection
  1764. // record identified by the connection guid.
  1765. //
  1766. FStatus = DbsReadTableRecordByIndex(ThreadCtx,
  1767. Replica,
  1768. TableCtx,
  1769. Cxtion->Name->Guid,
  1770. CrCxtionGuidx,
  1771. CXTIONTablex);
  1772. if (!FRS_SUCCESS(FStatus)) {
  1773. return FrsErrorBadOutLogPartnerData;
  1774. }
  1775. //
  1776. // this record should not be for an inbound partner.
  1777. //
  1778. if (Cxtion->Inbound) {
  1779. DPRINT(0, "ERROR - Can't get Outlog partner data from an imbound cxtion.\n");
  1780. return FrsErrorBadOutLogPartnerData;
  1781. }
  1782. OutLogPartner = Cxtion->OLCtx;
  1783. FRS_ASSERT(Cxtion->OLCtx);
  1784. FRS_ASSERT(OutLogPartner->Cxtion == Cxtion);
  1785. OutLogPartner->Flags = CxtionRecord->Flags;
  1786. //
  1787. // The restart point (COLxRestart) is where the leading index left off
  1788. // when the connection last shutdown. Set the active leading index (COLx)
  1789. // to the saved trailing index so we can resend any change orders that had
  1790. // not been Acked at the time the connection went down. They could have
  1791. // been lost in transit or if the destination crashed before writing them
  1792. // to the inbound log. Of course in the meantime Acks may come in for
  1793. // those COs if the outbound partner still has them. A few special checks
  1794. // are made elsewhere to detect this case and keep the leading index from
  1795. // falling behind the trailing index. See OutLogMarkAckVector().
  1796. //
  1797. // COLxVVJoinDone is similar to COLxRestart except that it applies only
  1798. // after a VVJoin has finished. The only behaviorial difference is that
  1799. // The out-of-order change order flag is not set because the normal mode
  1800. // change orders that get sent on the second OutLog pass of a VVJoin are
  1801. // sent in order. They should get dampened if a directed CO from the VV
  1802. // join has already sent the file.
  1803. //
  1804. OutLogPartner->COLxVVJoinDone = 0;
  1805. OutLogPartner->COLxRestart = CxtionRecord->COLx;
  1806. OutLogPartner->COLx = CxtionRecord->COTx;
  1807. OutLogPartner->COTx = CxtionRecord->COTx;
  1808. OutLogPartner->COTxNormalModeSave = CxtionRecord->COTxNormalModeSave;
  1809. OutLogPartner->COTslot = CxtionRecord->COTslot;
  1810. OutLogPartner->OutstandingQuota = MaxOutLogCoQuota; //CxtionRecord->OutstandingQuota
  1811. CopyMemory(OutLogPartner->AckVector, CxtionRecord->AckVector, ACK_VECTOR_BYTES);
  1812. OutLogPartner->AckVersion = CxtionRecord->AckVersion;
  1813. OutLogPartner->COTxLastSaved = OutLogPartner->COTx;
  1814. OutLogPartner->OutstandingCos = 0;
  1815. //
  1816. // If the change order leading index for this partner is greater than
  1817. // where we are currently inserting new records then advance to it.
  1818. // Use InterlockedCompareExchange to make sure it doesn't move backwards.
  1819. // This could happen if we get context switched and another thread advances
  1820. // the sequence number.
  1821. //
  1822. ADVANCE_VALUE_INTERLOCKED(&Replica->OutLogSeqNumber, OutLogPartner->COLx);
  1823. OUT_LOG_DUMP_PARTNER_STATE(4, OutLogPartner, OutLogPartner->COTx, "Reactivate");
  1824. //
  1825. // Check if we were already in vvjoin mode.
  1826. // Force a VV join in this case.
  1827. //
  1828. if (InVVJoinMode(OutLogPartner) || InOptVVJoinMode(OutLogPartner)) {
  1829. SetCxtionFlag(Cxtion, CXTION_FLAGS_PERFORM_VVJOIN);
  1830. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_VVJOIN_MODE);
  1831. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_OPT_VVJOIN_MODE);
  1832. DPRINT1(4, "Clearing vvjoin mode and optimized vvjoin mode (Flags are now %08x)\n",
  1833. OutLogPartner->Flags);
  1834. }
  1835. return FrsErrorSuccess;
  1836. }
  1837. ULONG
  1838. OutLogUpdatePartner(
  1839. IN PTHREAD_CTX ThreadCtx,
  1840. IN PTABLE_CTX TableCtx,
  1841. IN PREPLICA Replica,
  1842. IN PCXTION Cxtion
  1843. )
  1844. /*++
  1845. Routine Description:
  1846. Update the cxtion record in the database.
  1847. ** Warning ** This gets called for both inbound and outbound connections.
  1848. Arguments:
  1849. Replica -- The replica set struct for the outbound log.
  1850. Cxtion -- The information about the partner, Guid, Send Queue, Send
  1851. Quota, ...
  1852. Return Value:
  1853. Frs Status
  1854. --*/
  1855. {
  1856. #undef DEBSUB
  1857. #define DEBSUB "OutLogUpdatePartner:"
  1858. ULONG FStatus;
  1859. PCXTION_RECORD CxtionRecord = TableCtx->pDataRecord;
  1860. POUT_LOG_PARTNER OutLogPartner = Cxtion->OLCtx;
  1861. FRS_ASSERT(IS_CXTION_TABLE(TableCtx));
  1862. //
  1863. // Copy the fields from the cxtion into the table's cxtion record
  1864. //
  1865. OutLogCopyCxtionToCxtionRecord(Cxtion, TableCtx);
  1866. //
  1867. // Open the table and update the requested record.
  1868. //
  1869. FStatus = DbsUpdateTableRecordByIndex(ThreadCtx,
  1870. Replica,
  1871. TableCtx,
  1872. &CxtionRecord->CxtionGuid,
  1873. CrCxtionGuidx,
  1874. CXTIONTablex);
  1875. DBS_DISPLAY_RECORD_SEV(4, TableCtx, FALSE);
  1876. if (!FRS_SUCCESS(FStatus)) {
  1877. return FStatus;
  1878. }
  1879. NeedNewPartnerTable = TRUE;
  1880. //
  1881. // Track the value of the last Change Order Trailing Index saved for
  1882. // outbound connections.
  1883. //
  1884. if (!Cxtion->Inbound) {
  1885. OutLogPartner->COTxLastSaved = OutLogPartner->COTx;
  1886. }
  1887. return FrsErrorSuccess;
  1888. }
  1889. ULONG
  1890. OutLogProcess(
  1891. PVOID FrsThreadCtxArg
  1892. )
  1893. /*++
  1894. Routine Description:
  1895. Entry point for processing output log change orders.
  1896. Arguments:
  1897. FrsThreadCtxArg - thread
  1898. Return Value:
  1899. WIN32 Status
  1900. --*/
  1901. {
  1902. #undef DEBSUB
  1903. #define DEBSUB "OutLogProcess:"
  1904. JET_ERR jerr, jerr1;
  1905. ULONG WStatus = ERROR_SUCCESS;
  1906. ULONG FStatus;
  1907. NTSTATUS Status;
  1908. PFRS_THREAD FrsThread = (PFRS_THREAD)FrsThreadCtxArg;
  1909. PTHREAD_CTX ThreadCtx;
  1910. PTABLE_CTX TableCtx;
  1911. LIST_ENTRY DeadList;
  1912. PCOMMAND_PACKET CmdPkt;
  1913. PCOMMAND_PACKET Cmd;
  1914. PLIST_ENTRY Entry;
  1915. PREPLICA Replica;
  1916. PCXTION PartnerCxtion;
  1917. ULONG COx;
  1918. ULONG WaitTime;
  1919. BOOL CompleteCommand;
  1920. DPRINT(0, "Outbound log processor is starting.\n");
  1921. //
  1922. // The database must be started before we create a jet session
  1923. // WARN: the database startup code will be adding command
  1924. // packets to our queue while we are waiting. This should
  1925. // be okay.
  1926. // WARN: The database event may be set by the shutdown
  1927. // code in order to force threads to exit.
  1928. //
  1929. WaitForSingleObject(DataBaseEvent, INFINITE);
  1930. if (FrsIsShuttingDown) {
  1931. ShutDownOutLog();
  1932. goto EXIT_THREAD_NO_INIT;
  1933. }
  1934. //
  1935. // Try-Finally so we shutdown Jet cleanly.
  1936. //
  1937. try {
  1938. //
  1939. // Capture exception.
  1940. //
  1941. try {
  1942. //
  1943. // Allocate a context for Jet to run in this thread.
  1944. //
  1945. ThreadCtx = FrsAllocType(THREAD_CONTEXT_TYPE);
  1946. TableCtx = DbsCreateTableContext(CXTIONTablex);
  1947. //
  1948. // Setup a Jet Session returning the session ID in ThreadCtx.
  1949. //
  1950. jerr = DbsCreateJetSession(ThreadCtx);
  1951. if (JET_SUCCESS(jerr)) {
  1952. DPRINT(4,"JetOpenDatabase complete\n");
  1953. } else {
  1954. DPRINT_JS(0,"ERROR - OpenDatabase failed. Thread exiting.", jerr);
  1955. FStatus = DbsTranslateJetError(jerr, FALSE);
  1956. DbsFreeTableContext(TableCtx, 0);
  1957. jerr = DbsCloseJetSession(ThreadCtx);
  1958. ThreadCtx = FrsFreeType(ThreadCtx);
  1959. return ERROR_GEN_FAILURE;
  1960. }
  1961. DPRINT(0, "Outbound log processor has started.\n");
  1962. //
  1963. // Queue the cleanup command to the delayed command server.
  1964. //
  1965. Cmd = FrsAllocCommand(&OutLogWork, CMD_OUTLOG_CLEANUP);
  1966. RsTimeout(Cmd) = 0;
  1967. FrsDelQueueSubmit(Cmd, OUT_LOG_CLEAN_INTERVAL);
  1968. while(TRUE) {
  1969. Entry = FrsRtlRemoveHeadQueueTimeout(&OutLogWork, OUT_LOG_POLL_INTERVAL);
  1970. if (Entry == NULL) {
  1971. WStatus = GetLastError();
  1972. } else {
  1973. WStatus = ERROR_SUCCESS;
  1974. }
  1975. //
  1976. // Check the return code from remove queue.
  1977. //
  1978. if (WStatus == WAIT_TIMEOUT) {
  1979. DPRINT(5, "Wait timeout\n");
  1980. continue;
  1981. }
  1982. if (WStatus == ERROR_INVALID_HANDLE) {
  1983. DPRINT(1, "OutLog Queue was RunDown.\n");
  1984. //
  1985. // Queue was run down. Time to exit.
  1986. //
  1987. WStatus = ERROR_SUCCESS;
  1988. goto EXIT_LOOP;
  1989. }
  1990. //
  1991. // Some other error?
  1992. //
  1993. CLEANUP_WS(0, "Wait for queue error", WStatus, EXIT_LOOP);
  1994. //
  1995. // Check for a valid command packet.
  1996. //
  1997. CmdPkt = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry);
  1998. if (CmdPkt->Header.Type != COMMAND_PACKET_TYPE) {
  1999. DPRINT1(0, "ERROR - Unknown packet type %d\n", CmdPkt->Header.Type);
  2000. FrsCompleteCommand(CmdPkt, ERROR_GEN_FAILURE);
  2001. continue;
  2002. }
  2003. Replica = CmdPkt->Parameters.OutLogRequest.Replica;
  2004. PartnerCxtion = CmdPkt->Parameters.OutLogRequest.PartnerCxtion;
  2005. //
  2006. // If one of the commands below took an error then they may have
  2007. // called DbsFreeTableCtx(). Fix this here.
  2008. //
  2009. if (IS_INVALID_TABLE(TableCtx)) {
  2010. Status = DbsAllocTableCtx(CXTIONTablex, TableCtx);
  2011. if (!NT_SUCCESS(Status)) {
  2012. DPRINT_NT(0, "ERROR - DbsAllocRecordStorage failed to alloc buffers.", Status);
  2013. DbsFreeTableCtx(TableCtx, 1);
  2014. FrsCompleteCommand(CmdPkt, ERROR_GEN_FAILURE);
  2015. continue;
  2016. }
  2017. }
  2018. // Note: add try except around the command rather than process terminate
  2019. CompleteCommand = TRUE;
  2020. switch (CmdPkt->Command) {
  2021. case CMD_OUTLOG_WORK_CO:
  2022. DPRINT(5, "OutLogProcessReplica CALL <<<<<<<<<<<<<<<<<<<< \n");
  2023. if (Replica->OutLogWorkState == OL_REPLICA_NOPARTNERS) {
  2024. break;
  2025. }
  2026. //
  2027. // Process outbound log change orders for this Replica set.
  2028. //
  2029. FStatus = OutLogProcessReplica(ThreadCtx, Replica);
  2030. //
  2031. // If more work to do then requeue the command packet at the end
  2032. // to give other replica sets a chance.
  2033. //
  2034. if (FStatus == FrsErrorMoreWork) {
  2035. if (!FrsIsShuttingDown) {
  2036. FrsSubmitCommand(CmdPkt, FALSE);
  2037. FStatus = FrsErrorSuccess;
  2038. }
  2039. }
  2040. //
  2041. // If we finished all the work on this replica then we leave it off the
  2042. // queue. When more work arrives the cmd packet is re-submitted.
  2043. //
  2044. if (!FRS_SUCCESS(FStatus)) {
  2045. DPRINT_FS(0, "ERROR: OutLogProcessReplica failed.", FStatus);
  2046. WStatus = ERROR_GEN_FAILURE;
  2047. }
  2048. break;
  2049. case CMD_OUTLOG_ADD_REPLICA:
  2050. DPRINT(5, "OutLogAddReplica CALL <<<<<<<<<<<<<<<<<<<< \n");
  2051. FStatus = OutLogAddReplica(ThreadCtx, Replica);
  2052. break;
  2053. case CMD_OUTLOG_REMOVE_REPLICA:
  2054. DPRINT(5, "OutLogRemoveReplica CALL <<<<<<<<<<<<<<<<<<<< \n");
  2055. FStatus = OutLogRemoveReplica(ThreadCtx, Replica);
  2056. break;
  2057. case CMD_OUTLOG_INIT_PARTNER:
  2058. DPRINT(5, "OutLogInitPartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2059. FStatus = OutLogInitPartner(Replica, PartnerCxtion);
  2060. break;
  2061. case CMD_OUTLOG_ADD_NEW_PARTNER:
  2062. DPRINT(5, "OutLogAddNewPartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2063. FStatus = OutLogAddNewPartner(ThreadCtx, TableCtx, Replica, PartnerCxtion);
  2064. break;
  2065. case CMD_OUTLOG_UPDATE_PARTNER:
  2066. DPRINT(5, "OutLogUpdatePartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2067. FStatus = OutLogUpdatePartner(ThreadCtx, TableCtx, Replica, PartnerCxtion);
  2068. break;
  2069. case CMD_OUTLOG_DEACTIVATE_PARTNER:
  2070. DPRINT(5, "OutLogDeactivatePartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2071. FStatus = OutLogDeactivatePartner(ThreadCtx, TableCtx, Replica, PartnerCxtion);
  2072. break;
  2073. case CMD_OUTLOG_ACTIVATE_PARTNER:
  2074. DPRINT(5, "OutLogActivatePartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2075. FStatus = OutLogActivatePartnerCmd(ThreadCtx,
  2076. TableCtx,
  2077. Replica,
  2078. PartnerCxtion,
  2079. FALSE);
  2080. break;
  2081. case CMD_OUTLOG_CLOSE_PARTNER:
  2082. DPRINT(5, "OutLogClosePartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2083. FStatus = OutLogClosePartner(ThreadCtx, TableCtx, Replica, PartnerCxtion);
  2084. break;
  2085. case CMD_OUTLOG_REMOVE_PARTNER:
  2086. DPRINT(5, "OutLogRemovePartner CALL <<<<<<<<<<<<<<<<<<<< \n");
  2087. FStatus = OutLogRemovePartner(ThreadCtx, TableCtx, Replica, PartnerCxtion);
  2088. break;
  2089. case CMD_OUTLOG_RETIRE_CO:
  2090. DPRINT(5, "OutLogRetireCo CALL <<<<<<<<<<<<<<<<<<<< \n");
  2091. COx = CmdPkt->Parameters.OutLogRequest.SequenceNumber;
  2092. FStatus = OutLogRetireCo(Replica, COx, PartnerCxtion);
  2093. break;
  2094. case CMD_OUTLOG_CLEANUP:
  2095. DPRINT(5, "OutLogCleanup CALL <<<<<<<<<<<<<<<<<<<< \n");
  2096. FStatus = OutLogCleanup(ThreadCtx, CmdPkt);
  2097. if (FRS_SUCCESS(FStatus)) {
  2098. CompleteCommand = FALSE;
  2099. }
  2100. break;
  2101. default:
  2102. DPRINT1(0, "ERROR - Unknown OutLog command %d\n", (ULONG)CmdPkt->Command);
  2103. FStatus = FrsErrorInvalidOperation;
  2104. } // end switch
  2105. //
  2106. // Retire the command packet. (Note if this is our "work" packet the
  2107. // completion routine is a no-op).
  2108. //
  2109. if (CompleteCommand == TRUE) {
  2110. FrsCompleteCommand(CmdPkt, FStatus);
  2111. }
  2112. continue;
  2113. EXIT_LOOP:
  2114. break;
  2115. }
  2116. } except (EXCEPTION_EXECUTE_HANDLER) {
  2117. GET_EXCEPTION_CODE(WStatus);
  2118. }
  2119. } finally {
  2120. //
  2121. // Shutdown
  2122. //
  2123. if (WIN_SUCCESS(WStatus)) {
  2124. if (AbnormalTermination()) {
  2125. WStatus = ERROR_OPERATION_ABORTED;
  2126. }
  2127. }
  2128. DPRINT_WS(0, "Outlog finally.", WStatus);
  2129. //
  2130. // Do outbound log cleanup on each replica if something happened.
  2131. //
  2132. ForEachListEntry( &ReplicaListHead, REPLICA, ReplicaList,
  2133. // Loop iterator pE is type PREPLICA.
  2134. if (pE->OutLogDoCleanup) {
  2135. DPRINT3(4, "OutLog Cleanup for replica %ws, id: %d, (%08x)\n",
  2136. pE->ReplicaName->Name, pE->ReplicaNumber, pE);
  2137. OutLogCleanupLog(ThreadCtx, pE);
  2138. }
  2139. //
  2140. // Close down the connection info on each outbound partner.
  2141. //
  2142. OutLogRemoveReplica(ThreadCtx, pE);
  2143. );
  2144. DbsFreeTableContext(TableCtx, 0);
  2145. //
  2146. // Now close the jet session and free the Jet ThreadCtx.
  2147. //
  2148. jerr = DbsCloseJetSession(ThreadCtx);
  2149. if (!JET_SUCCESS(jerr)) {
  2150. DPRINT_JS(0,"DbsCloseJetSession error:", jerr);
  2151. } else {
  2152. DPRINT(4,"DbsCloseJetSession complete\n");
  2153. }
  2154. ThreadCtx = FrsFreeType(ThreadCtx);
  2155. }
  2156. EXIT_THREAD_NO_INIT:
  2157. DPRINT1(3, "<<<<<<<...T E R M I N A T I N G -- %s...>>>>>>>>\n", DEBSUB);
  2158. DPRINT(0, "Outbound log processor is exiting.\n");
  2159. //
  2160. // Trigger FRS shutdown if we terminated abnormally.
  2161. //
  2162. if (!WIN_SUCCESS(WStatus)) {
  2163. DPRINT(0, "Outbound log processor terminated abnormally, rundown outlog queue.\n");
  2164. ShutDownOutLog();
  2165. DPRINT(0, "Outbound log processor terminated abnormally, forcing service shutdown.\n");
  2166. FrsIsShuttingDown = TRUE;
  2167. SetEvent(ShutDownEvent);
  2168. }
  2169. ThSupSubmitThreadExitCleanup(FrsThreadCtxArg);
  2170. ExitThread(WStatus);
  2171. return ERROR_SUCCESS;
  2172. }
  2173. ULONG
  2174. OutLogProcessReplica(
  2175. PTHREAD_CTX ThreadCtx,
  2176. PREPLICA Replica
  2177. )
  2178. /*++
  2179. Routine Description:
  2180. New work has arrived for this replica. This could be due to a new change
  2181. order arriving in the outbound log or an outbound partner could have
  2182. acknowledged a change order making it eligible to receive the next one.
  2183. There could be many change orders queued for this replica set. To keep
  2184. this replica set from consuming resources to the exclusion of other
  2185. replica sets we process a limited number of change orders and then return
  2186. with stats FrsErrorMoreWork. This causes the command packet to be requeued
  2187. at the end of the OutLogWork queue so we can service other replica sets.
  2188. Arguments:
  2189. ThreadCtx -- A Thread context to use for dbid and sesid.
  2190. Replica -- The replica set struct for the outbound log.
  2191. Return Value:
  2192. Frs Status
  2193. FrsErrorMoreWork - if there is more work to do on this Replica Set.
  2194. FrsErrorSuccess - if there is no more work or no eligible outbound
  2195. parnters for this replica set.
  2196. --*/
  2197. {
  2198. #undef DEBSUB
  2199. #define DEBSUB "OutLogProcessReplica:"
  2200. ULONG FStatus;
  2201. PLIST_ENTRY Entry;
  2202. POUT_LOG_PARTNER Partner;
  2203. ULONG JointLeadingIndex;
  2204. PCHANGE_ORDER_COMMAND CoCmd;
  2205. BOOL MoreCo;
  2206. ULONG LoopCheck = 0;
  2207. //
  2208. // Get the outbound log lock for this replica and hold it until we
  2209. // are finished. If this is a performance problem with the Ack side
  2210. // then split the lock.
  2211. //
  2212. OutLogAcquireLock(Replica);
  2213. Replica->OutLogRepeatInterval = GOutLogRepeatInterval;
  2214. //
  2215. // A delay before processing the OutLog can give frequently changing files
  2216. // a chance to get into the DominantFileTable. The question is what is
  2217. // the right delay and where is the right place to put it?
  2218. //
  2219. // Sleep(20*1000);
  2220. START_OVER:
  2221. JointLeadingIndex = 0xFFFFFFFF;
  2222. if (Replica->OutLogWorkState == OL_REPLICA_NOPARTNERS) {
  2223. OutLogReleaseLock(Replica);
  2224. return FrsErrorSuccess;
  2225. }
  2226. //
  2227. // Move standby partner entries to the Eligible list.
  2228. //
  2229. ForEachSimpleListEntry(&Replica->OutLogStandBy, OUT_LOG_PARTNER, List,
  2230. // Iterator pE is type *OUT_LOG_PARTNER
  2231. FrsRemoveEntryList(&pE->List);
  2232. pE->State = OLP_ELIGIBLE;
  2233. InsertTailList(&Replica->OutLogEligible, &pE->List);
  2234. );
  2235. //
  2236. // Find the Joint Leading Index for the current group of Eligible partners.
  2237. //
  2238. ForEachSimpleListEntry(&Replica->OutLogEligible, OUT_LOG_PARTNER, List,
  2239. // Iterator pE is type *OUT_LOG_PARTNER
  2240. //
  2241. // A zero in COLx could mean any of the following:
  2242. // 1. This is the first time the partner has ever joined
  2243. // 2. A previous VV Join was interrupted
  2244. // 3. Change order's destined for cxtion were trimmed from the log.
  2245. // 4. The last join time on this connection does not match
  2246. // (implies DB inconsistency)
  2247. // so do a VVJoin.
  2248. //
  2249. if (pE->COLx == 0) {
  2250. FStatus = OutLogPartnerVVJoinStart(ThreadCtx, Replica, pE);
  2251. if (!FRS_SUCCESS(FStatus)) {
  2252. DPRINT_FS(0, "Error return from OutLogPartnerVVJoinStart:", FStatus);
  2253. FrsRemoveEntryList(&pE->List);
  2254. InsertTailList(&Replica->OutLogActive, &pE->List);
  2255. FRS_PRINT_TYPE(0, pE);
  2256. continue;
  2257. }
  2258. }
  2259. if (pE->COLx < JointLeadingIndex) {
  2260. JointLeadingIndex = pE->COLx;
  2261. }
  2262. );
  2263. DPRINT1(4, "JointLeadingIndex = 0x%x\n", JointLeadingIndex);
  2264. if (IsListEmpty(&Replica->OutLogEligible)) {
  2265. DPRINT(4, "OutLogEligible list is empty\n");
  2266. }
  2267. //
  2268. // Send change orders to the partners on the eligible list.
  2269. //
  2270. MoreCo = TRUE;
  2271. while (!IsListEmpty(&Replica->OutLogEligible) && !FrsIsShuttingDown) {
  2272. FStatus = OutLogReadCo(ThreadCtx, Replica, JointLeadingIndex);
  2273. if (FStatus == FrsErrorRecordLocked) {
  2274. //
  2275. // We hit the end of the log. That's it for now. Change the
  2276. // Replica state to waiting if nothing appeared on the standby list.
  2277. //
  2278. MoreCo = FALSE;
  2279. break;
  2280. } else
  2281. if (FStatus == FrsErrorNotFound) {
  2282. //
  2283. // Deleted log record. Update ack vector and leading index
  2284. // for each eligible partner and go to the next record.
  2285. // The change order has already been sent and cleaned up.
  2286. //
  2287. OutLogSkipCo(Replica, JointLeadingIndex);
  2288. } else
  2289. if (FRS_SUCCESS(FStatus)){
  2290. //
  2291. // Process the change order.
  2292. // Clear IFLAG bits we don't want to send to outbound partner.
  2293. // Save the Ack sequence number in the CO command so the outbound
  2294. // partner can return it to us. It's in CO command record so it
  2295. // lives across CO retries in the outbound partner.
  2296. //
  2297. CoCmd = (PCHANGE_ORDER_COMMAND) Replica->OutLogTableCtx->pDataRecord;
  2298. ClearFlag(CoCmd->IFlags, CO_IFLAG_GROUP_OL_CLEAR);
  2299. CoCmd->PartnerAckSeqNumber = CoCmd->SequenceNumber;
  2300. //
  2301. // Send the change order to each Eligible partner.
  2302. //
  2303. Entry = GetListHead(&Replica->OutLogEligible);
  2304. while( Entry != &Replica->OutLogEligible) {
  2305. Partner = CONTAINING_RECORD(Entry, OUT_LOG_PARTNER, List);
  2306. Entry = GetListNext(Entry);
  2307. if(OutLogSendCo(ThreadCtx,
  2308. Replica,
  2309. Partner,
  2310. CoCmd,
  2311. JointLeadingIndex)) {
  2312. LoopCheck += 1;
  2313. FRS_ASSERT(LoopCheck < 1000);
  2314. goto START_OVER;
  2315. }
  2316. }
  2317. } else {
  2318. //
  2319. // When outlog triming is added see if the jet read is getting
  2320. // JET_errNoCurrentRecord (which otherwise is mapped to FrsErrorInternalError
  2321. //
  2322. DPRINT_FS(0, "ERROR - Unexpected return from OutLogReadCo.", FStatus);
  2323. MoreCo = FALSE;
  2324. break;
  2325. }
  2326. JointLeadingIndex += 1;
  2327. } // while loop over change orders.
  2328. //
  2329. // Check the Eligible or Standby lists for more work.
  2330. //
  2331. if (IsListEmpty(&Replica->OutLogStandBy) &&
  2332. (!MoreCo || IsListEmpty(&Replica->OutLogEligible)) ) {
  2333. FStatus = FrsErrorSuccess;
  2334. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_WAITING);
  2335. } else {
  2336. FStatus = FrsErrorMoreWork;
  2337. }
  2338. OutLogReleaseLock(Replica);
  2339. return FStatus;
  2340. }
  2341. BOOL
  2342. OutLogSendCo(
  2343. PTHREAD_CTX ThreadCtx,
  2344. PREPLICA Replica,
  2345. POUT_LOG_PARTNER Partner,
  2346. PCHANGE_ORDER_COMMAND CoCmd,
  2347. ULONG JointLeadingIndex
  2348. )
  2349. /*++
  2350. Routine Description:
  2351. Send the change order to the specified outbound partner.
  2352. Assumes caller has the OutLog lock.
  2353. Arguments:
  2354. ThreadCtx -- A Thread context to use for dbid and sesid.
  2355. Replica -- The replica set struct for the outbound log.
  2356. Partner -- ptr to outbound partner context
  2357. CoCmd -- Change order to send
  2358. JointLeadingIndex -- Sequence number of OutLog CO being sent.
  2359. Return Value:
  2360. TRUE - State change requires caller to reevaluate JointLeadingIndex.
  2361. FALSE - Request processed normally
  2362. --*/
  2363. {
  2364. #undef DEBSUB
  2365. #define DEBSUB "OutLogSendCo:"
  2366. #define OLSEND_UPDATE_JLX (TRUE)
  2367. #define OLSEND_KEEP_JLX (FALSE)
  2368. ULONG FStatus;
  2369. BOOL CoDampened, SendIt;
  2370. BOOL AdjustCOLx;
  2371. PCXTION Cxtion;
  2372. BOOL ReevaluateJLx = OLSEND_KEEP_JLX;
  2373. PCHAR SendTag = "NotSent";
  2374. ULONG jerr;
  2375. TABLE_CTX TempTableCtx;
  2376. PTABLE_CTX TableCtx = &TempTableCtx;
  2377. //
  2378. // If the Ack Vector has wrapped such that we are still waiting
  2379. // for an Ack from the change order in the next slot then we
  2380. // have to stall until the Ack comes in. This can happen if
  2381. // the CO is fetching a large file or we run into a slug of
  2382. // dampened COs which we quickly run thru.
  2383. //
  2384. if (AVWrapped(Partner)) {
  2385. SET_OUTLOG_PARTNER_AVWRAP(Replica, Partner);
  2386. FRS_PRINT_TYPE(1, Partner);
  2387. CHANGE_ORDER_TRACE2_OLOG(3, CoCmd, "AVWrap ", Replica, Partner->Cxtion);
  2388. //
  2389. // Force a rejoin if the ack vector remains wrapped and there
  2390. // is no network activity from this cxtion's partner. The
  2391. // unacked cos will be resent when the cxtion is rejoined.
  2392. //
  2393. Cxtion = Partner->Cxtion;
  2394. if (Cxtion &&
  2395. CxtionStateIs(Cxtion, CxtionStateJoined)) {
  2396. RcsSubmitReplicaCxtion(Replica, Cxtion, CMD_HUNG_CXTION);
  2397. }
  2398. return OLSEND_UPDATE_JLX;
  2399. }
  2400. //
  2401. // If we have already sent this CO to the partner then move on
  2402. // to the next partner in the list.
  2403. //
  2404. if (JointLeadingIndex < Partner->COLx) {
  2405. CHANGE_ORDER_TRACE2_OLOG(5, CoCmd, "Skip JLx<COLx ", Replica, Partner->Cxtion);
  2406. return OLSEND_KEEP_JLX;
  2407. }
  2408. FRS_ASSERT(JointLeadingIndex == Partner->COLx);
  2409. //
  2410. // Send out the CO. The Ack Vector bit could be set if this is
  2411. // a reactivation of the partner or the partner has recently left
  2412. // VVJoin mode. Don't send the CO again.
  2413. // The CO could also be dampened later based on the version vector.
  2414. //
  2415. CoDampened = SendIt = FALSE;
  2416. if (ReadAVBit(Partner->COLx, Partner) == 1) {
  2417. //
  2418. // The AckVector bit was one. Clear it and don't send CO.
  2419. //
  2420. ClearAVBit(Partner->COLx, Partner);
  2421. SendTag = "Skip AV=1";
  2422. goto SEND;
  2423. }
  2424. //
  2425. // If this connection is in VVJoin Mode then only send it
  2426. // directed change orders. Otherwise send out all COs.
  2427. //
  2428. if (!BooleanFlagOn(CoCmd->Flags, CO_FLAG_DIRECTED_CO)) {
  2429. //
  2430. // This is a normal CO.
  2431. // If destination partner is in VVJoin Mode then don't send.
  2432. //
  2433. if (InVVJoinMode(Partner)) {
  2434. DPRINT3(4, "%-11ws (%08x %08x): Partner in VVJoin Mode, hold normal COs - "
  2435. "Cxtion: %ws\\%ws\\%ws to %ws\\%ws.\n",
  2436. CoCmd->FileName, PRINTQUAD(CoCmd->FrsVsn),
  2437. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2438. SendTag = "Skip INVVJoin";
  2439. } else {
  2440. if (InReplayMode(Partner) && VVHasVsn(Partner->Cxtion->VVector, CoCmd)) {
  2441. //
  2442. // If we are in the replay range of a completed VVJoin then mark
  2443. // this CO as out of order if it is going to be dampened by the
  2444. // VV on the cxtion.
  2445. // If while we are scanning through the idtable to build tables
  2446. // for vvjoin 2 COs come in the system. The one with lower VSN
  2447. // updates/creates a entry in the idtable before our current scan point
  2448. // and the other CO with higher VSN updates/creates entry at a point
  2449. // ahead of the current scan point. We will send the CO for the higher
  2450. // VSN which will update the VV for that connection as part of vvjoin.
  2451. // Later when we do out replay we will dampen the CO with the lower VSN
  2452. // which has never been sent to the downstream partner.
  2453. //
  2454. SetFlag(CoCmd->Flags, CO_FLAG_OUT_OF_ORDER);
  2455. }
  2456. SendIt = TRUE;
  2457. }
  2458. goto SEND;
  2459. }
  2460. //
  2461. // This is a directed CO.
  2462. // If it is not for this connection then don't send it.
  2463. //
  2464. if (!GUIDS_EQUAL(Partner->Cxtion->Name->Guid, &CoCmd->CxtionGuid)) {
  2465. DPRINT3(4, "%-11ws (%08x %08x): Not sending directed CO to "
  2466. "Cxtion: %ws\\%ws\\%ws to %ws\\%ws.\n",
  2467. CoCmd->FileName, PRINTQUAD(CoCmd->FrsVsn),
  2468. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2469. SendTag = "Skip DirCO";
  2470. goto SEND;
  2471. }
  2472. //
  2473. // This is a directed CO for this connection. If we are in the
  2474. // replay range of a completed VVJoin then don't send it again.
  2475. // NOTE: This only works if no directed COs are sent to this
  2476. // outbound partner other than VVJoin COs while in VVJoin Mode.
  2477. // This is currently true. Refresh change order requests don't
  2478. // go through the outbound log.
  2479. //
  2480. if (Partner->COLx < Partner->COLxVVJoinDone) {
  2481. // This CO was already sent.
  2482. SendTag = "Skip COLx<VVJoinDone";
  2483. goto SEND;
  2484. }
  2485. //
  2486. // Send it but check if it is a control change order first.
  2487. //
  2488. SendIt = TRUE;
  2489. DPRINT3(4, "%-11ws (%08x %08x): Sending directed CO to Cxtion: %ws\\%ws\\%ws to %ws\\%ws.\n",
  2490. CoCmd->FileName, PRINTQUAD(CoCmd->FrsVsn),
  2491. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2492. if (BooleanFlagOn(CoCmd->Flags, CO_FLAG_CONTROL)) {
  2493. //
  2494. // Currently don't prop control change orders.
  2495. //
  2496. SendIt = FALSE;
  2497. //
  2498. // Check for a control CO that is terminating a VVJoin on this connection.
  2499. //
  2500. if ((CoCmd->ContentCmd == FCN_CO_NORMAL_VVJOIN_TERM) ||
  2501. (CoCmd->ContentCmd == FCN_CO_ABNORMAL_VVJOIN_TERM)) {
  2502. //
  2503. // If this is a normal termination and we are currently in replay mode
  2504. // then come out of the replay mode.
  2505. //
  2506. if (InReplayMode(Partner) && (CoCmd->ContentCmd == FCN_CO_NORMAL_VVJOIN_TERM)) {
  2507. ClearFlag(Partner->Flags, OLP_FLAGS_REPLAY_MODE);
  2508. //
  2509. // Open the connection table and update the partner state.
  2510. //
  2511. TableCtx->TableType = TABLE_TYPE_INVALID;
  2512. TableCtx->Tid = JET_tableidNil;
  2513. jerr = DbsOpenTable(ThreadCtx, TableCtx, Replica->ReplicaNumber, CXTIONTablex, NULL);
  2514. if (!JET_SUCCESS(jerr)) {
  2515. DPRINT1_JS(0, "DbsOpenTable (cxtion) on replica number %d failed.",
  2516. Replica->ReplicaNumber, jerr);
  2517. }
  2518. OutLogSaveSinglePartnerState(ThreadCtx, Replica, TableCtx, Partner);
  2519. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  2520. DbsFreeTableCtx(TableCtx, 1);
  2521. FRS_PRINT_TYPE(4, Partner);
  2522. DPRINT1(4, "Replay mode completed: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2523. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2524. goto SEND;
  2525. }
  2526. if (!InVVJoinMode(Partner)) {
  2527. DPRINT1(4, "Not in vvjoin mode: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2528. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2529. goto SEND;
  2530. }
  2531. //
  2532. // Losing track of the outstanding cos can result in lost change
  2533. // orders for a trigger cxtion. So wait for the outstanding change
  2534. // orders to finish.
  2535. //
  2536. if (Partner->OutstandingCos) {
  2537. //
  2538. // Pretend it is at quota to reuse an existing codepath
  2539. //
  2540. DPRINT2(4, "WARN - Waiting for %d Cos at vvjoindone: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2541. Partner->OutstandingCos, PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2542. SET_OUTLOG_PARTNER_AT_QUOTA(Replica, Partner);
  2543. return OLSEND_UPDATE_JLX;
  2544. }
  2545. FStatus = OutLogPartnerVVJoinDone(ThreadCtx, Replica, Partner);
  2546. if (!FRS_SUCCESS(FStatus)) {
  2547. DPRINT_FS(0, "Error return from OutLogPartnerVVJoinDone:", FStatus);
  2548. FrsRemoveEntryList(&Partner->List);
  2549. InsertTailList(&Replica->OutLogActive, &Partner->List);
  2550. FRS_PRINT_TYPE(0, Partner);
  2551. return OLSEND_KEEP_JLX;
  2552. }
  2553. //
  2554. // If termination was abnormal then start it up again.
  2555. //
  2556. if (CoCmd->ContentCmd == FCN_CO_ABNORMAL_VVJOIN_TERM) {
  2557. FStatus = OutLogPartnerVVJoinStart(ThreadCtx, Replica, Partner);
  2558. if (!FRS_SUCCESS(FStatus)) {
  2559. DPRINT_FS(0, "Error return from OutLogPartnerVVJoinStart:", FStatus);
  2560. FrsRemoveEntryList(&Partner->List);
  2561. InsertTailList(&Replica->OutLogActive, &Partner->List);
  2562. FRS_PRINT_TYPE(0, Partner);
  2563. return OLSEND_KEEP_JLX;
  2564. }
  2565. }
  2566. //
  2567. // Leading index was changed for connection reevaluate JointLeadingIndex.
  2568. //
  2569. return OLSEND_UPDATE_JLX;
  2570. }
  2571. if (CoCmd->ContentCmd == FCN_CO_END_OF_JOIN) {
  2572. if (Partner->Cxtion->PartnerMinor < NTFRS_COMM_MINOR_3) {
  2573. DPRINT3(2, "WARN - Downrev partner (%d (them) < %d (us)): %ws\\%ws\\%ws -> %ws\\%ws\n",
  2574. Partner->Cxtion->PartnerMinor, NtFrsCommMinor,
  2575. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2576. //
  2577. // This control co is for this join and the join is still valid
  2578. //
  2579. } else
  2580. if (CoCmd->EventTime.QuadPart == (LONGLONG)Partner->Cxtion->LastJoinTime &&
  2581. CxtionStateIs(Partner->Cxtion, CxtionStateJoined)) {
  2582. if (Partner->OutstandingCos > 0) {
  2583. //
  2584. // Pretend it is at quota to reuse an existing codepath
  2585. //
  2586. DPRINT2(4, "WARN - Waiting for %d Cos at end of join: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2587. Partner->OutstandingCos, PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2588. SET_OUTLOG_PARTNER_AT_QUOTA(Replica, Partner);
  2589. return OLSEND_UPDATE_JLX;
  2590. } else {
  2591. DPRINT1(4, "Unjoining at end of join: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2592. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2593. //
  2594. // Stop sending change orders and unjoin the cxtion
  2595. //
  2596. SET_OUTLOG_PARTNER_UNJOINED(Replica, Partner);
  2597. RcsSubmitReplicaCxtion(Replica, Partner->Cxtion, CMD_UNJOIN);
  2598. }
  2599. } else {
  2600. DPRINT1(4, "Ignoring; end-of-join guid invalid: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2601. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2602. }
  2603. } else if (CoCmd->ContentCmd == FCN_CO_END_OF_OPTIMIZED_VVJOIN) {
  2604. //
  2605. // This control co is for this join and the join is still valid
  2606. //
  2607. if (CoCmd->EventTime.QuadPart == (LONGLONG)Partner->Cxtion->LastJoinTime &&
  2608. CxtionStateIs(Partner->Cxtion, CxtionStateJoined) &&
  2609. InOptVVJoinMode(Partner)) {
  2610. if (Partner->OutstandingCos > 0) {
  2611. //
  2612. // Pretend it is at quota to reuse an existing codepath
  2613. //
  2614. DPRINT2(4, "WARN - Waiting for %d Cos at end-of-opt-vvjoin: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2615. Partner->OutstandingCos, PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2616. //
  2617. // We have to send this multiple times because we do not have
  2618. // a mechanism to reliably deliver this message to the downstream
  2619. // partner.
  2620. //
  2621. DPRINT1(4, "Sending vvjoinsuccess at end-of-opt-vvjoin : %ws\\%ws\\%ws -> %ws\\%ws\n",
  2622. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2623. RcsSubmitReplicaCxtion(Replica, Partner->Cxtion, CMD_VVJOIN_SUCCESS);
  2624. SET_OUTLOG_PARTNER_AT_QUOTA(Replica, Partner);
  2625. return OLSEND_UPDATE_JLX;
  2626. } else {
  2627. DPRINT1(4, "Sending vvjoinsuccess at end-of-opt-vvjoin : %ws\\%ws\\%ws -> %ws\\%ws\n",
  2628. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2629. RcsSubmitReplicaCxtion(Replica, Partner->Cxtion, CMD_VVJOIN_SUCCESS);
  2630. ClearFlag(Partner->Flags, OLP_FLAGS_OPT_VVJOIN_MODE);
  2631. //
  2632. // Open the connection table and update the partner state.
  2633. //
  2634. TableCtx->TableType = TABLE_TYPE_INVALID;
  2635. TableCtx->Tid = JET_tableidNil;
  2636. jerr = DbsOpenTable(ThreadCtx, TableCtx, Replica->ReplicaNumber, CXTIONTablex, NULL);
  2637. if (!JET_SUCCESS(jerr)) {
  2638. DPRINT1_JS(0, "DbsOpenTable (cxtion) on replica number %d failed.",
  2639. Replica->ReplicaNumber, jerr);
  2640. }
  2641. OutLogSaveSinglePartnerState(ThreadCtx, Replica, TableCtx, Partner);
  2642. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  2643. DbsFreeTableCtx(TableCtx, 1);
  2644. FRS_PRINT_TYPE(4, Partner);
  2645. }
  2646. } else {
  2647. DPRINT1(4, "Ignoring; end-of-opt-vvjoin guid invalid: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2648. PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2649. }
  2650. } else {
  2651. DPRINT2(0, "WARN - Ignoring bad control code %d: %ws\\%ws\\%ws -> %ws\\%ws\n",
  2652. CoCmd->ContentCmd, PRINT_CXTION_PATH(Replica, Partner->Cxtion));
  2653. }
  2654. }
  2655. SEND:
  2656. //
  2657. // Send the CO if enabled. If it was dampened then set the Ack flag here.
  2658. //
  2659. AdjustCOLx = TRUE;
  2660. ReevaluateJLx = OLSEND_KEEP_JLX;
  2661. if (SendIt) {
  2662. //
  2663. // If this connection is being restarted and the leading
  2664. // index is less than the Restart leading index then mark
  2665. // the CO out of order so it can get past VV dampening checks.
  2666. // We don't know what may have been sent and acked ahead of
  2667. // this point.
  2668. //
  2669. if (Partner->COLx < Partner->COLxRestart) {
  2670. SetFlag(CoCmd->Flags, CO_FLAG_OUT_OF_ORDER);
  2671. }
  2672. //
  2673. // Finally check to see if there is a more recent change order for
  2674. // this file in the OutLogDominantFileTable for this Replica Set.
  2675. //
  2676. SendIt = OutLogOptimize(Replica, Partner, CoCmd, &SendTag);
  2677. if (!SendIt) {
  2678. goto SEND;
  2679. }
  2680. //
  2681. // Increment the Local OR Remote CO Sent counters for both the
  2682. // replica set and the connection.
  2683. //
  2684. if (COC_FLAG_ON(CoCmd, CO_FLAG_LOCALCO)) {
  2685. //
  2686. // Its a Local CO
  2687. //
  2688. PM_INC_CTR_REPSET(Replica, LCOSent, 1);
  2689. PM_INC_CTR_CXTION(Partner->Cxtion, LCOSent, 1);
  2690. }
  2691. else if (!COC_FLAG_ON(CoCmd, CO_FLAG_CONTROL)) {
  2692. //
  2693. // Its a Remote CO
  2694. //
  2695. PM_INC_CTR_REPSET(Replica, RCOSent, 1);
  2696. PM_INC_CTR_CXTION(Partner->Cxtion, RCOSent, 1);
  2697. }
  2698. //
  2699. // Set the Ack Vector Version number into the change order for match
  2700. // up later when the Ack comes in.
  2701. //
  2702. CoCmd->AckVersion = Partner->AckVersion;
  2703. CoDampened = !RcsSendCoToOneOutbound(Replica, Partner->Cxtion, CoCmd);
  2704. if (CoDampened) {
  2705. SendTag = "VVDampened";
  2706. //
  2707. // Increment the OutBound CO dampned counter for both the
  2708. // replica set and the connection.
  2709. //
  2710. PM_INC_CTR_REPSET(Replica, OutCODampned, 1);
  2711. PM_INC_CTR_CXTION(Partner->Cxtion, OutCODampned, 1);
  2712. }
  2713. } else {
  2714. CHANGE_ORDER_TRACE2_OLOG(3, CoCmd, SendTag, Replica, Partner->Cxtion);
  2715. SendTag = NULL;
  2716. }
  2717. if (CoDampened || !SendIt) {
  2718. //
  2719. // CO was dampened. Set the Ack flag in the ack vector and
  2720. // advance the trailing index for this partner. The bits
  2721. // behind the trailing index are cleared.
  2722. //
  2723. AdjustCOLx = OutLogMarkAckVector(Replica, Partner->COLx, Partner);
  2724. } else {
  2725. //
  2726. // It was sent. If the partner has hit its quota of outstanding
  2727. // COs remove it move it from the Eligible list to the Active List.
  2728. // Have the caller reevaluate the joint leading index so it can jump
  2729. // ahead to the next CO to send.
  2730. //
  2731. Partner->OutstandingCos += 1;
  2732. if (Partner->OutstandingCos >= Partner->OutstandingQuota) {
  2733. SET_OUTLOG_PARTNER_AT_QUOTA(Replica, Partner);
  2734. ReevaluateJLx = OLSEND_UPDATE_JLX;
  2735. }
  2736. SendTag = "Send";
  2737. }
  2738. if (SendTag != NULL) {
  2739. CHANGE_ORDER_TRACE2_OLOG(3, CoCmd, SendTag, Replica, Partner->Cxtion);
  2740. }
  2741. if (AdjustCOLx) {
  2742. Partner->COLx += 1;
  2743. }
  2744. //
  2745. // Save the max Outlog progress point for error checks.
  2746. //
  2747. if (Partner->COLx > Replica->OutLogCOMax) {
  2748. Replica->OutLogCOMax = Partner->COLx;
  2749. }
  2750. OUT_LOG_DUMP_PARTNER_STATE(4, Partner, Partner->COLx-1,
  2751. (CoDampened || !SendIt ? "dampened" : "send"));
  2752. return ReevaluateJLx;
  2753. }
  2754. BOOL
  2755. OutLogOptimize(
  2756. IN PREPLICA Replica,
  2757. IN POUT_LOG_PARTNER Partner,
  2758. IN PCHANGE_ORDER_COMMAND CoCmd,
  2759. OUT PCHAR *SendTag
  2760. )
  2761. /*++
  2762. Routine Description:
  2763. Finally check to see if there is a more recent change order for
  2764. this file in the OutLogDominantFileTable and that this CO is not
  2765. already in our MustSend Table for this connection.
  2766. Assumes caller has the OutLog lock.
  2767. Arguments:
  2768. Replica -- The replica set struct for the outbound log.
  2769. Partner -- ptr to outbound partner context
  2770. CoCmd -- Change order to send
  2771. SendTag -- return a ptr to a tag string for logging.
  2772. Return Value:
  2773. TRUE - This CO must be sent out.
  2774. FALSE - This CO can be skipped.
  2775. --*/
  2776. {
  2777. #undef DEBSUB
  2778. #define DEBSUB "OutLogOptimize:"
  2779. ULONGLONG DeltaTime;
  2780. PQHASH_ENTRY QHashEntry;
  2781. PDOMINANT_FILE_ENTRY MustSendEntry, DomFileEntry;
  2782. //
  2783. // If not a valid candidate for skipping then send it.
  2784. //
  2785. // TODO: If the CO in the dominant file table is a delete then we should
  2786. // favor it and suppress a create CO. This will evaporate a
  2787. // create - delete sequence.
  2788. //
  2789. if ((Replica->OutLogRepeatInterval == 0) ||
  2790. !OutLogFavorDominantFile(CoCmd)) {
  2791. goto SEND_IT;
  2792. }
  2793. QHashAcquireLock(Partner->MustSendTable);
  2794. QHashEntry = QHashLookupLock(Partner->MustSendTable, &CoCmd->FileGuid);
  2795. if (QHashEntry != NULL) {
  2796. //
  2797. // Found a match check if this is our MustSend CO.
  2798. //
  2799. DPRINT1(4, "OPT: hit in MustSend Table for COx 0x%x\n", CoCmd->SequenceNumber);
  2800. MustSendEntry = (PDOMINANT_FILE_ENTRY) (QHashEntry->Flags);
  2801. if (MustSendEntry->OLSeqNum > CoCmd->SequenceNumber) {
  2802. //
  2803. // Not yet.
  2804. //
  2805. QHashReleaseLock(Partner->MustSendTable);
  2806. *SendTag = "Skip, Not Dominant";
  2807. goto DO_NOT_SEND;
  2808. }
  2809. if (MustSendEntry->TimeSent > 0) {
  2810. //
  2811. // We have sent a CO for this File in the past.
  2812. // Do not send another for at least RepeatInterval seconds if
  2813. // there is a more recent CO in the DominantFileTable.
  2814. //
  2815. GetSystemTimeAsFileTime((PFILETIME)&DeltaTime);
  2816. DeltaTime -= MustSendEntry->TimeSent;
  2817. DeltaTime /= (ULONGLONG)(10 * 1000 * 1000);
  2818. if (DeltaTime < Replica->OutLogRepeatInterval) {
  2819. //
  2820. // We sent one less than RepeatInterval seconds ago. Don't
  2821. // send this one if we have a later one in the Dominant
  2822. // File Table.
  2823. //
  2824. QHashAcquireLock(Replica->OutLogDominantTable);
  2825. QHashEntry = QHashLookupLock(Replica->OutLogDominantTable,
  2826. &CoCmd->FileGuid);
  2827. if (QHashEntry != NULL) {
  2828. DomFileEntry = (PDOMINANT_FILE_ENTRY) (QHashEntry->Flags);
  2829. if (DomFileEntry->OLSeqNum > CoCmd->SequenceNumber){
  2830. //
  2831. // There is a later one so skip this one. But don't
  2832. // update the MustSendEntry since the later one may
  2833. // be a long distance back in the queue and we may hit
  2834. // another CO for this file in the mean time but after
  2835. // the RepeatInterval has been exceeded.
  2836. //
  2837. *SendTag = "Skip, To soon to send";
  2838. QHashReleaseLock(Replica->OutLogDominantTable);
  2839. QHashReleaseLock(Partner->MustSendTable);
  2840. goto DO_NOT_SEND;
  2841. }
  2842. }
  2843. QHashReleaseLock(Replica->OutLogDominantTable);
  2844. }
  2845. }
  2846. //
  2847. // No dominant CO found. Send this one.
  2848. //
  2849. MustSendEntry->OLSeqNum = CoCmd->SequenceNumber;
  2850. GetSystemTimeAsFileTime((PFILETIME)&MustSendEntry->TimeSent);
  2851. QHashReleaseLock(Partner->MustSendTable);
  2852. goto SEND_IT;
  2853. }
  2854. //
  2855. // No entry in MustSendTable for this connection.
  2856. // Check for an entry in the OutLog Dominant File Table.
  2857. //
  2858. DPRINT1(4, "OPT: miss in MustSend Table for COx 0x%x\n", CoCmd->SequenceNumber);
  2859. QHashAcquireLock(Replica->OutLogDominantTable);
  2860. QHashEntry = QHashLookupLock(Replica->OutLogDominantTable, &CoCmd->FileGuid);
  2861. if (QHashEntry != NULL) {
  2862. DomFileEntry = (PDOMINANT_FILE_ENTRY) (QHashEntry->Flags);
  2863. //if (DomFileEntry->OLSeqNum >= CoCmd->SequenceNumber) {
  2864. //
  2865. // This CO can be skipped, but make entry in the MustSend table
  2866. // so we have the time sent for future checks.
  2867. //
  2868. MustSendEntry = FrsAlloc(sizeof(DOMINANT_FILE_ENTRY));
  2869. if (MustSendEntry != NULL) {
  2870. memcpy(MustSendEntry, DomFileEntry, sizeof(DOMINANT_FILE_ENTRY));
  2871. MustSendEntry->Flags = 0;
  2872. MustSendEntry->TimeSent = 0;
  2873. QHashEntry = QHashInsertLock(Partner->MustSendTable,
  2874. &CoCmd->FileGuid,
  2875. NULL,
  2876. (ULONG_PTR) MustSendEntry);
  2877. if (QHashEntry != NULL) {
  2878. DPRINT1(4, "OPT: new entry made in MustSend Table for COx 0x%x\n",
  2879. CoCmd->SequenceNumber);
  2880. if (DomFileEntry->OLSeqNum != CoCmd->SequenceNumber){
  2881. //
  2882. // We can skip it since there is a later one.
  2883. // Still made the entry above so we have the
  2884. // TimeSent for future checks.
  2885. //
  2886. QHashReleaseLock(Replica->OutLogDominantTable);
  2887. QHashReleaseLock(Partner->MustSendTable);
  2888. *SendTag = "Skip, New Dominant";
  2889. goto DO_NOT_SEND;
  2890. } else {
  2891. GetSystemTimeAsFileTime((PFILETIME)&MustSendEntry->TimeSent);
  2892. }
  2893. } else {
  2894. DPRINT(4, "++ WARN - Failed to insert entry into Partner MustSendTable\n");
  2895. FrsFree(MustSendEntry);
  2896. }
  2897. }
  2898. //}
  2899. } else {
  2900. DPRINT1(4, "OPT: miss in Dom Table for COx 0x%x\n", CoCmd->SequenceNumber);
  2901. }
  2902. QHashReleaseLock(Replica->OutLogDominantTable);
  2903. QHashReleaseLock(Partner->MustSendTable);
  2904. SEND_IT:
  2905. return TRUE;
  2906. DO_NOT_SEND:
  2907. PM_INC_CTR_REPSET(Replica, OutCODampned, 1);
  2908. PM_INC_CTR_CXTION(Partner->Cxtion, OutCODampned, 1);
  2909. return FALSE;
  2910. }
  2911. VOID
  2912. OutLogSkipCo(
  2913. PREPLICA Replica,
  2914. ULONG JointLeadingIndex
  2915. )
  2916. /*++
  2917. Routine Description:
  2918. The change order at this index has been deleted so skip over it
  2919. for all eligible outbound partners.
  2920. Assumes caller has the OutLog lock.
  2921. Arguments:
  2922. Replica -- The replica set struct for the outbound log.
  2923. JointLeadingIndex -- Sequence number of OutLog CO being skiped.
  2924. Return Value:
  2925. None.
  2926. --*/
  2927. {
  2928. #undef DEBSUB
  2929. #define DEBSUB "OutLogSkipCo:"
  2930. ULONG FStatus;
  2931. PLIST_ENTRY Entry;
  2932. POUT_LOG_PARTNER Partner;
  2933. BOOL AdjustCOLx;
  2934. //
  2935. // Skip over the CO for each Eligible partner.
  2936. //
  2937. Entry = GetListHead(&Replica->OutLogEligible);
  2938. while( Entry != &Replica->OutLogEligible) {
  2939. Partner = CONTAINING_RECORD(Entry, OUT_LOG_PARTNER, List);
  2940. Entry = GetListNext(Entry);
  2941. //
  2942. // If the Ack Vector has wrapped such that we are still waiting
  2943. // for an Ack from the change order in the next slot then we
  2944. // have to stall until the Ack comes in. This can happen if
  2945. // the CO is fetching a large file or we run into a slug of
  2946. // dampened COs which we quickly run thru.
  2947. //
  2948. // Note: can not wait forever. need to force a rejoin at some
  2949. // point. Integrate this with outlog trimming since both will
  2950. // force a VVJoin.
  2951. //
  2952. if (AVWrapped(Partner)) {
  2953. SET_OUTLOG_PARTNER_AVWRAP(Replica, Partner);
  2954. FRS_PRINT_TYPE(1, Partner);
  2955. continue;
  2956. }
  2957. //
  2958. // If we have already sent this CO to the partner then move on
  2959. // to the next partner in the list.
  2960. //
  2961. if (JointLeadingIndex < Partner->COLx) {
  2962. continue;
  2963. }
  2964. FRS_ASSERT(JointLeadingIndex == Partner->COLx);
  2965. //
  2966. // The Ack Vector bit could be set if this is
  2967. // a reactivation of the partner or the partner has recently left
  2968. // VVJoin mode.
  2969. //
  2970. if (ReadAVBit(Partner->COLx, Partner) == 1) {
  2971. //
  2972. // The AckVector bit was one, clear it.
  2973. //
  2974. ClearAVBit(Partner->COLx, Partner);
  2975. }
  2976. //
  2977. // CO was skipped. Set the Ack flag in the ack vector and
  2978. // advance the trailing index for this partner. The bits
  2979. // behind the trailing index are cleared.
  2980. //
  2981. AdjustCOLx = OutLogMarkAckVector(Replica, Partner->COLx, Partner);
  2982. if (AdjustCOLx) {
  2983. Partner->COLx += 1;
  2984. }
  2985. //
  2986. // Save the max Outlog progress point for error checks.
  2987. //
  2988. if (Partner->COLx > Replica->OutLogCOMax) {
  2989. Replica->OutLogCOMax = Partner->COLx;
  2990. }
  2991. //OUT_LOG_DUMP_PARTNER_STATE(4, Partner, Partner->COLx-1, "Co Deleted");
  2992. } // while on eligible list
  2993. return;
  2994. }
  2995. ULONG
  2996. OutLogCommitPartnerState(
  2997. IN PTHREAD_CTX ThreadCtx,
  2998. IN PTABLE_CTX TableCtx,
  2999. IN PREPLICA Replica,
  3000. IN PCXTION Cxtion
  3001. )
  3002. /*++
  3003. Routine Description:
  3004. Update the database with the current state for the specified partner.
  3005. Arguments:
  3006. ThreadCtx
  3007. TableCtx
  3008. Replica -- The replica set struct for the outbound log.
  3009. Cxtion -- The Outbound Partner that is Acking the change order.
  3010. Return Value:
  3011. Frs Status
  3012. --*/
  3013. {
  3014. #undef DEBSUB
  3015. #define DEBSUB "OutLogCommitPartnerState:"
  3016. return OutLogUpdatePartner(ThreadCtx, TableCtx, Replica, Cxtion);
  3017. }
  3018. ULONG
  3019. OutLogReadCo(
  3020. PTHREAD_CTX ThreadCtx,
  3021. PREPLICA Replica,
  3022. ULONG Index
  3023. )
  3024. /*++
  3025. Routine Description:
  3026. Read the change order specified by the Index from the Outbound Log for
  3027. the Replica. The data is returned in the Replica->OutLogTableCtx struct.
  3028. Arguments:
  3029. ThreadCtx -- A Thread context to use for dbid and sesid.
  3030. Replica -- The replica set struct for the outbound log.
  3031. Index -- The Index / Sequence Number to use to select the change order.
  3032. Return Value:
  3033. Frs Status
  3034. --*/
  3035. {
  3036. #undef DEBSUB
  3037. #define DEBSUB "OutLogReadCo:"
  3038. ULONG FStatus;
  3039. PTABLE_CTX TableCtx = Replica->OutLogTableCtx;
  3040. ULONGLONG Data;
  3041. FRS_ASSERT(IS_OUTLOG_TABLE(TableCtx));
  3042. // Note: Consider a record cache to reduce calls to Jet.
  3043. QHashAcquireLock(Replica->OutLogRecordLock);
  3044. //
  3045. // First check if the outlog record is currently being written or we are
  3046. // at the end of the log.
  3047. //
  3048. Data = Index;
  3049. if ((Index != 0) &&
  3050. ((Index >= Replica->OutLogSeqNumber) ||
  3051. (QHashLookupLock(Replica->OutLogRecordLock, &Data) != NULL))) {
  3052. DPRINT3(3, "OutLog Record lock on Index %08x %08x (Index %08x, Replica %08x)\n",
  3053. PRINTQUAD(Data), Index, Replica->OutLogSeqNumber);
  3054. QHashReleaseLock(Replica->OutLogRecordLock);
  3055. return FrsErrorRecordLocked;
  3056. }
  3057. QHashReleaseLock(Replica->OutLogRecordLock);
  3058. //
  3059. // Open the outbound log table for the replica set and read the requested
  3060. // record identified by the sequence number.
  3061. //
  3062. FStatus = DbsReadTableRecordByIndex(ThreadCtx,
  3063. Replica,
  3064. TableCtx,
  3065. &Index,
  3066. OLSequenceNumberIndexx,
  3067. OUTLOGTablex);
  3068. if (!FRS_SUCCESS(FStatus)) {
  3069. if (FStatus == FrsErrorNotFound) {
  3070. //
  3071. // No record at this sequence number, probably deleted.
  3072. //
  3073. DPRINT1(4, "Record 0x%x deleted\n", Index);
  3074. }
  3075. return FStatus;
  3076. }
  3077. //DUMP_TABLE_CTX(OutLogTableCtx);
  3078. DBS_DISPLAY_RECORD_SEV(4, TableCtx, TRUE);
  3079. return FrsErrorSuccess;
  3080. }
  3081. ULONG
  3082. OutLogDeleteCo(
  3083. PTHREAD_CTX ThreadCtx,
  3084. PREPLICA Replica,
  3085. ULONG Index
  3086. )
  3087. /*++
  3088. Routine Description:
  3089. Delete the change order specified by the Index from the Outbound Log for
  3090. the Replica. This uses the common Replica->OutLogTableCtx so it must
  3091. be called by the thread that is doing work for this Replica.
  3092. NOTE - THIS IS ONLY OK IF WE KNOW WHICH THREAD IS WORKING ON THE REPLICA
  3093. OR THERE IS ONLY ONE OUTBOUND LOG PROCESS THREAD.
  3094. Arguments:
  3095. ThreadCtx -- A Thread context to use for dbid and sesid.
  3096. Replica -- The replica set struct for the outbound log.
  3097. Index -- The Index / Sequence Number to use to select the change order.
  3098. Return Value:
  3099. Frs Status
  3100. --*/
  3101. {
  3102. #undef DEBSUB
  3103. #define DEBSUB "OutLogDeleteCo:"
  3104. return DbsDeleteTableRecordByIndex(ThreadCtx,
  3105. Replica,
  3106. Replica->OutLogTableCtx,
  3107. &Index,
  3108. OLSequenceNumberIndexx,
  3109. OUTLOGTablex);
  3110. }
  3111. ULONG
  3112. OutLogInsertCo(
  3113. PTHREAD_CTX ThreadCtx,
  3114. PREPLICA Replica,
  3115. PTABLE_CTX OutLogTableCtx,
  3116. PCHANGE_ORDER_ENTRY ChangeOrder
  3117. )
  3118. /*++
  3119. Routine Description:
  3120. Insert the change order into the outbound log. This call should only be
  3121. made after the Inbound change order has been completed and the IDTable
  3122. state is updated. After this call succeeds the caller should then
  3123. delete the record from the Inbound Log.
  3124. Note - This is where the sequence number for the record is assigned.
  3125. Note - This is called by ChgOrdIssueCleanup() in a database thread.
  3126. Arguments:
  3127. ThreadCtx -- A Thread context to use for dbid and sesid.
  3128. Replica -- The replica set struct for the outbound log.
  3129. OutLogTableCtx -- The table context to use for outbound log table access.
  3130. ChangeOrder -- The new change order to check ordering conflicts.
  3131. Return Value:
  3132. Frs Status
  3133. --*/
  3134. {
  3135. #undef DEBSUB
  3136. #define DEBSUB "OutLogInsertCo:"
  3137. ULONGLONG Data;
  3138. JET_ERR jerr;
  3139. ULONG FStatus = FrsErrorSuccess;
  3140. ULONG GStatus;
  3141. PCHANGE_ORDER_COMMAND CoCmd = &ChangeOrder->Cmd;
  3142. ULONG SequenceNumberSave;
  3143. PDOMINANT_FILE_ENTRY DomFileEntry;
  3144. PQHASH_ENTRY QHashEntry;
  3145. CHAR GuidStr[GUID_CHAR_LEN];
  3146. PDATA_EXTENSION_RETRY_TIMEOUT CoCmdRetryTimeout = NULL;
  3147. LONGLONG CurrentTime;
  3148. LONGLONG FirstTryTimeSave = (LONGLONG)0;
  3149. //
  3150. // Insert the change order into the outbound log. Log Cleanup will delete
  3151. // the staging file after it has been sent to all the partners.
  3152. //
  3153. // ** Note ** - The Replica Outlog Sequence number is at the max of all
  3154. // partner leading indexs and the record in the log with the largest
  3155. // sequence number+1. The interlocked increment is done first to get
  3156. // the next sequence number and then one is subtracted from the result.
  3157. //
  3158. // ** Note ** This change order command is an inbound change order so for
  3159. // consistency and correctness in subsequent operations we save and restore
  3160. // the Sequence Number around this call. The alternative is to make a copy
  3161. // of the data record first.
  3162. //
  3163. SequenceNumberSave = CoCmd->SequenceNumber;
  3164. //
  3165. // Save the replica ptrs by converting them to local replica ID numbers
  3166. // for storing the record in the database.
  3167. //
  3168. CoCmd->OriginalReplicaNum = ReplicaAddrToId(ChangeOrder->OriginalReplica);
  3169. CoCmd->NewReplicaNum = ReplicaAddrToId(ChangeOrder->NewReplica);
  3170. //
  3171. // Use the Change Order extension to store the time the CO was inserted into
  3172. // the outbound log. Save the current value and restore it after inserting the CO.
  3173. // Use the extension only if it is present. VVjoin cos and controls cos do not
  3174. // have the extension.
  3175. if (CoCmd->Extension != NULL) {
  3176. CoCmdRetryTimeout = DbsDataExtensionFind(CoCmd->Extension, DataExtend_Retry_Timeout);
  3177. if(CoCmdRetryTimeout != NULL) {
  3178. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  3179. FirstTryTimeSave = CoCmdRetryTimeout->FirstTryTime;
  3180. CoCmdRetryTimeout->FirstTryTime = CurrentTime;
  3181. }
  3182. }
  3183. //
  3184. // Get the Outlog record lock and add this sequence number to it.
  3185. // This is needed so the outlog process can tell if it hit the end of log
  3186. // if it does a read and gets back record not found. This lock table
  3187. // allows it to distinguish between a missing outlog change order that
  3188. // will require a VVJoin Scan for the partner from a change order outlog
  3189. // write that hasn't finished yet.
  3190. //
  3191. QHashAcquireLock(Replica->OutLogRecordLock);
  3192. // Perf Note: If we keep the lock table then get rid of the interlocked ops
  3193. CoCmd->SequenceNumber = InterlockedIncrement(&Replica->OutLogSeqNumber) - 1;
  3194. Data = CoCmd->SequenceNumber;
  3195. QHashInsertLock(Replica->OutLogRecordLock, &Data, &Data, 0);
  3196. QHashReleaseLock(Replica->OutLogRecordLock);
  3197. //
  3198. // Open the Outbound log table.
  3199. //
  3200. if (!IS_TABLE_OPEN(OutLogTableCtx)) {
  3201. jerr = DbsOpenTable(ThreadCtx,
  3202. OutLogTableCtx,
  3203. Replica->ReplicaNumber,
  3204. OUTLOGTablex,
  3205. CoCmd);
  3206. if (!JET_SUCCESS(jerr)) {
  3207. FStatus = DbsTranslateJetError(jerr, TRUE);
  3208. goto RETURN;
  3209. }
  3210. }
  3211. //
  3212. // Update the OutLogDominantTable as necessary.
  3213. //
  3214. Replica->OutLogRepeatInterval = GOutLogRepeatInterval;
  3215. DPRINT1(4, "OPT: Replica OutLogRepeatInterval = %d\n", Replica->OutLogRepeatInterval);
  3216. if ((Replica->OutLogRepeatInterval > 0) &&
  3217. OutLogIsValidDominantFile(CoCmd)) {
  3218. QHashAcquireLock(Replica->OutLogDominantTable);
  3219. DPRINT1(4, "OPT: valid Dom File for COx 0x%x\n", CoCmd->SequenceNumber);
  3220. QHashEntry = QHashLookupLock(Replica->OutLogDominantTable, &CoCmd->FileGuid);
  3221. if (QHashEntry != NULL) {
  3222. //
  3223. // Found a match, bump the count and record latest sequence number.
  3224. //
  3225. DomFileEntry = (PDOMINANT_FILE_ENTRY) (QHashEntry->Flags);
  3226. DPRINT2(4, "OPT: hit in Dom Table for new COx 0x%x, old COx 0x%x\n",
  3227. CoCmd->SequenceNumber, DomFileEntry->OLSeqNum);
  3228. QHashEntry->QData += 1;
  3229. DomFileEntry->OLSeqNum = CoCmd->SequenceNumber;
  3230. QHashReleaseLock(Replica->OutLogDominantTable);
  3231. } else {
  3232. //
  3233. // Not found in Dominant Table, do the OutLog lookup.
  3234. // Note: The record is not read from the table so we don't know
  3235. // if the CO found actually meets all the requirements for
  3236. // skipping. That check is made when we actually try to send the
  3237. // CO. The entries in the OutLogDominantTable do not have to meet
  3238. // the requirements for skipping. We only want to know that there
  3239. // is a future CO for this File that we will be sending if a previous
  3240. // CO can be skipped. So a sequence like update a large file followed
  3241. // by a delete of the file will only send the delete.
  3242. //
  3243. QHashReleaseLock(Replica->OutLogDominantTable);
  3244. DPRINT1(4, "OPT: miss in Dom Table for COx 0x%x\n", CoCmd->SequenceNumber);
  3245. jerr = DbsSeekRecord(ThreadCtx,
  3246. &CoCmd->FileGuid,
  3247. OLFileGuidIndexx,
  3248. OutLogTableCtx);
  3249. if (JET_SUCCESS(jerr)) {
  3250. //
  3251. // Found another CO with the same file Guid. Add an entry to the
  3252. // DominantFileTable.
  3253. //
  3254. GuidToStr(&CoCmd->FileGuid, GuidStr);
  3255. DPRINT3(4, "Found new dominant file entry for replica %ws file: %ws (%s)\n",
  3256. Replica->ReplicaName->Name, CoCmd->FileName, GuidStr);
  3257. DomFileEntry = FrsAlloc(sizeof(DOMINANT_FILE_ENTRY));
  3258. if (DomFileEntry != NULL) {
  3259. DomFileEntry->Flags = 0;
  3260. COPY_GUID(&DomFileEntry->FileGuid, &CoCmd->FileGuid);
  3261. DomFileEntry->OLSeqNum = CoCmd->SequenceNumber;
  3262. if (DOES_CO_DELETE_FILE_NAME(CoCmd)) {
  3263. SetFlag(DomFileEntry->Flags, DFT_FLAG_DELETE);
  3264. }
  3265. DPRINT1(4, "OPT: Insert new Dom File for COx 0x%x\n", CoCmd->SequenceNumber);
  3266. GStatus = QHashInsert(Replica->OutLogDominantTable,
  3267. &CoCmd->FileGuid,
  3268. NULL,
  3269. (ULONG_PTR) DomFileEntry,
  3270. FALSE);
  3271. if (GStatus != GHT_STATUS_SUCCESS) {
  3272. DPRINT2(4, "++ ERROR - Failed to insert entry into Replica OutLogDominant Table for %ws (%s)",
  3273. CoCmd->FileName, GuidStr);
  3274. }
  3275. }
  3276. } else {
  3277. DPRINT1_JS(4, "OPT: Seek for Dom File for COx 0x%x failed", CoCmd->SequenceNumber, jerr);
  3278. }
  3279. }
  3280. }
  3281. //
  3282. // Insert the new CO record into the database.
  3283. //
  3284. jerr = DbsInsertTable2(OutLogTableCtx);
  3285. if (!JET_SUCCESS(jerr)) {
  3286. DPRINT_JS(1, "error inserting outlog record:", jerr);
  3287. FStatus = DbsTranslateJetError(jerr, TRUE);
  3288. DBS_DISPLAY_RECORD_SEV(5, OutLogTableCtx, FALSE);
  3289. DUMP_TABLE_CTX(OutLogTableCtx);
  3290. }
  3291. DbsCloseTable(jerr, ThreadCtx->JSesid, OutLogTableCtx);
  3292. DPRINT_JS(0,"Error - JetCloseTable failed:", jerr);
  3293. RETURN:
  3294. //
  3295. // Release the record lock.
  3296. //
  3297. if (QHashDelete(Replica->OutLogRecordLock, &Data) != GHT_STATUS_SUCCESS) {
  3298. DPRINT(0, "Error deleting outlog lock table entry\n");
  3299. FRS_ASSERT(!"Error deleting outlog lock table entry");
  3300. }
  3301. CoCmd->SequenceNumber = SequenceNumberSave;
  3302. //
  3303. // Restore the original FirstTryTime in case it is being used.
  3304. //
  3305. if(CoCmdRetryTimeout != NULL) {
  3306. CoCmdRetryTimeout->FirstTryTime = FirstTryTimeSave;
  3307. }
  3308. //
  3309. // Don't free the data record when we free the OutLogTableCtx.
  3310. // The data record is part of the change order.
  3311. //
  3312. OutLogTableCtx->pDataRecord = NULL;
  3313. //
  3314. // Clear the Jet Set/Ret Col address fields for the Change Order
  3315. // Extension buffer to prevent reuse since that buffer goes with the CO.
  3316. //
  3317. DBS_SET_FIELD_ADDRESS(OutLogTableCtx, COExtensionx, NULL);
  3318. if (!FRS_SUCCESS(FStatus)) {
  3319. return FStatus;
  3320. }
  3321. //
  3322. // Poke the Outbound log processor.
  3323. //
  3324. OutLogStartProcess(Replica);
  3325. return FrsErrorSuccess;
  3326. }
  3327. ULONG
  3328. OutLogStartProcess(
  3329. PREPLICA Replica
  3330. )
  3331. /*++
  3332. Routine Description:
  3333. If Outbound log processing for this replica is waiting then queue
  3334. the start work command packet to crank it up.
  3335. Arguments:
  3336. Replica -- The replica set struct for the outbound log.
  3337. Return Value:
  3338. Frs Status
  3339. --*/
  3340. {
  3341. #undef DEBSUB
  3342. #define DEBSUB "OutLogStartProcess:"
  3343. if (FrsIsShuttingDown) {
  3344. return FrsErrorSuccess;
  3345. }
  3346. if (Replica->OutLogWorkState == OL_REPLICA_WAITING) {
  3347. //
  3348. // Get the lock and recheck.
  3349. //
  3350. OutLogAcquireLock(Replica);
  3351. if (Replica->OutLogWorkState == OL_REPLICA_WAITING) {
  3352. SET_OUTLOG_REPLICA_STATE(Replica, OL_REPLICA_WORKING);
  3353. FrsSubmitCommand(Replica->OutLogCmdPkt, FALSE);
  3354. }
  3355. OutLogReleaseLock(Replica);
  3356. }
  3357. return FrsErrorSuccess;
  3358. }
  3359. ULONG
  3360. OutLogSubmitCo(
  3361. PREPLICA Replica,
  3362. PCHANGE_ORDER_ENTRY ChangeOrder
  3363. )
  3364. /*++
  3365. Routine Description:
  3366. Send the change order to the Outbound Log process to insert it into
  3367. the log and send it to the outbound partners.
  3368. Make a check for a pending change order in the outbound log that applies
  3369. to the same file. If found and not currently active we delete the
  3370. change order and staging file since this change order will send the
  3371. file again. Even if the change order has been sent but the file has
  3372. not been fetched or a fetch is in progress we should be able to abort
  3373. the fetch with an error response such that the fetching partner will
  3374. abort the change order. Note that it still is expected to send the
  3375. ACK response indicating the CO is retired.
  3376. Note - If there are no outbound partners defined for this replica set
  3377. this call is a nop.
  3378. Arguments:
  3379. Replica -- The replica set struct for the outbound log.
  3380. ChangeOrder -- The new change order to check ordering conflicts.
  3381. Return Value:
  3382. Frs Status
  3383. --*/
  3384. {
  3385. #undef DEBSUB
  3386. #define DEBSUB "OutLogSubmitCo:"
  3387. ULONG WStatus;
  3388. return FrsErrorSuccess;
  3389. }
  3390. ULONG
  3391. OutLogRetireCo(
  3392. PREPLICA Replica,
  3393. ULONG COx,
  3394. PCXTION PartnerCxtion
  3395. )
  3396. /*++
  3397. Routine Description:
  3398. The specified outbound partner is Acking the change order. Set the bit in
  3399. the AckVector and advance the trailing change order index. Add the
  3400. partner back to the eligible list if necc.
  3401. Arguments:
  3402. Replica -- The replica set struct for the outbound log.
  3403. COx -- The sequence number / index of the change order to retire.
  3404. Partner -- The Outbound Partner that is Acking the change order.
  3405. Return Value:
  3406. Frs Status
  3407. --*/
  3408. {
  3409. #undef DEBSUB
  3410. #define DEBSUB "OutLogRetireCo:"
  3411. POUT_LOG_PARTNER OutLogPartner = PartnerCxtion->OLCtx;
  3412. OutLogAcquireLock(Replica);
  3413. //
  3414. // Make sure the index of the retiring change order makes sense.
  3415. //
  3416. if (COx > Replica->OutLogCOMax) {
  3417. DPRINT2(0, "WARNING: COx (0x%x) > Replica->OutLogCOMax (0x%x)\n",
  3418. COx, Replica->OutLogCOMax);
  3419. }
  3420. //
  3421. // Check if this is a duplicate Ack. Could happen if outbound partner has
  3422. // crashed and is restarting.
  3423. //
  3424. if ((COx < OutLogPartner->COTx) ||
  3425. (ReadAVBit(COx, OutLogPartner) != 0)) {
  3426. OutLogReleaseLock(Replica);
  3427. return FrsErrorSuccess;
  3428. }
  3429. //
  3430. // Set the Ack flag in the ack vector and advance the trailing index for
  3431. // this partner.
  3432. //
  3433. OutLogMarkAckVector(Replica, COx, OutLogPartner);
  3434. //
  3435. // Decrement the count of outstanding change orders for this partner.
  3436. // If the partner is at the Quota limit then queue the partner struct to
  3437. // either the Eligible or Standby lists depending on the outbound log
  3438. // processing state of this replica.
  3439. //
  3440. // If we crash and come back up we could still have change orders out
  3441. // that acks are comming back for. Since we don't know how many
  3442. // we make sure the OutstandingCo count doesn't go below zero.
  3443. //
  3444. if (OutLogPartner->OutstandingCos > 0) {
  3445. OutLogPartner->OutstandingCos -= 1;
  3446. }
  3447. // Perf Note: Add code to test if the COLx is equal to the max change order seq
  3448. // number for this replica so we suppress queueing the the cmd pkt
  3449. // just to discover there is no pending COs for this replica.
  3450. if (OutLogPartner->State == OLP_AT_QUOTA) {
  3451. //
  3452. // Note - if we ever do dynamic adjustment of OutstandingQuotas then
  3453. // this assert must be fixed.
  3454. //
  3455. FRS_ASSERT(OutLogPartner->OutstandingCos < OutLogPartner->OutstandingQuota);
  3456. //
  3457. // Reactivate this partner since it is now below quota.
  3458. //
  3459. OutLogActivatePartner(Replica, PartnerCxtion, TRUE);
  3460. }
  3461. OutLogReleaseLock(Replica);
  3462. return FrsErrorSuccess;
  3463. }
  3464. BOOL
  3465. OutLogMarkAckVector(
  3466. PREPLICA Replica,
  3467. ULONG COx,
  3468. POUT_LOG_PARTNER OutLogPartner
  3469. )
  3470. /*++
  3471. Routine Description:
  3472. The specified outbound partner is Acking the change order. Set the bit in
  3473. the AckVector and advance the trailing change order index.
  3474. Note: The caller must acquire the outbound log lock.
  3475. Arguments:
  3476. Replica -- The replica set struct for the outbound log.
  3477. COx -- The sequence number / index of the change order to retire.
  3478. Partner -- The Outbound Partner that is Acking the change order.
  3479. Return Value:
  3480. TRUE if the caller should update COLx if appropriate.
  3481. FALSE if COLx was adjusted here then caller should leave it alone.
  3482. --*/
  3483. {
  3484. #undef DEBSUB
  3485. #define DEBSUB "OutLogMarkAckVector:"
  3486. ULONG Slotx, MaxSlotx;
  3487. BOOL CxtionRestarting, CxtionVVJoining;
  3488. BOOL AdjustCOLx = TRUE;
  3489. //
  3490. // Check if COx is outside the range of the Ack Vector. If it is then
  3491. // ignore it. This could happen when a partner is out of date and needs
  3492. // to do a VVJoin. When the VVJoin terminates we restart at the point in
  3493. // the outlog where the VVJoin started. This could be a long way back and
  3494. // we could get Acks for VVJoin COs sent that are still ahead of us in the
  3495. // Outlog. It could also happen when the start of a VVJoin advances the
  3496. // outlog index for the connection to the end of the Outlog. We could still
  3497. // get some Acks dribbling in for old change orders that are now just
  3498. // finishing from this outbound partner.
  3499. //
  3500. if (SeqNumOutsideAVWindow(COx, OutLogPartner)) {
  3501. DPRINT1(4, "Ack sequence number, 0x%x is outside current AV window. Ignored\n",
  3502. COx);
  3503. OUT_LOG_DUMP_PARTNER_STATE(4, OutLogPartner, COx, "Outside AVWindow");
  3504. return TRUE;
  3505. }
  3506. OUT_LOG_DUMP_PARTNER_STATE(4, OutLogPartner, COx, "Retire (Start)");
  3507. //
  3508. // Set the bit in the Ack Vector at the Change order index.
  3509. //
  3510. SetAVBit(COx, OutLogPartner);
  3511. Slotx = AVSlot(COx, OutLogPartner);
  3512. //
  3513. // If this was the trailing change order index then advance it to the
  3514. // slot of the next unacknowledged change order.
  3515. //
  3516. if (Slotx == OutLogPartner->COTslot) {
  3517. MaxSlotx = Slotx + ACK_VECTOR_SIZE;
  3518. while (++Slotx < MaxSlotx) {
  3519. //
  3520. // As the trailing index is advanced clear the bits behind it.
  3521. // Stop when we hit the next 0 bit.
  3522. //
  3523. OutLogPartner->COTx += 1;
  3524. ClearAVBitBySlot(Slotx-1, OutLogPartner);
  3525. //
  3526. // If this connection is restarting then COLx could be left behind
  3527. // COTx if an ACK comes in for a CO that was sent prior to the
  3528. // connection shutdown. This can only happen until COLx catches
  3529. // back up to COLxRestart. During this period keep COLx up with COTx.
  3530. //
  3531. if (OutLogPartner->COLx < (OutLogPartner->COTx-1)) {
  3532. CxtionRestarting = OutLogPartner->COLx <= OutLogPartner->COLxRestart;
  3533. CxtionVVJoining = OutLogPartner->COLx <= OutLogPartner->COLxVVJoinDone;
  3534. if (!CxtionRestarting && !CxtionVVJoining) {
  3535. OUT_LOG_DUMP_PARTNER_STATE(0,
  3536. OutLogPartner,
  3537. OutLogPartner->COTx, "Bug");
  3538. FRS_ASSERT(!"COLx < COTx but Cxtion not Restarting or VVJoining");
  3539. }
  3540. OutLogPartner->COLx = OutLogPartner->COTx;
  3541. AdjustCOLx = FALSE;
  3542. OUT_LOG_DUMP_PARTNER_STATE(0,
  3543. OutLogPartner,
  3544. OutLogPartner->COTx, "Bug2");
  3545. }
  3546. if (ReadAVBitBySlot(Slotx, OutLogPartner) == 0) {
  3547. break;
  3548. }
  3549. }
  3550. OutLogPartner->COTslot = Slotx & (ACK_VECTOR_SIZE-1);
  3551. Replica->OutLogDoCleanup = TRUE;
  3552. }
  3553. OUT_LOG_DUMP_PARTNER_STATE(4,
  3554. OutLogPartner,
  3555. OutLogPartner->COTx,
  3556. "Retire (end)");
  3557. return AdjustCOLx;
  3558. }
  3559. ULONG
  3560. OutLogSaveSinglePartnerState(
  3561. IN PTHREAD_CTX ThreadCtx,
  3562. IN PREPLICA Replica,
  3563. IN PTABLE_CTX TableCtx,
  3564. IN POUT_LOG_PARTNER OutLogPartner
  3565. )
  3566. /*++
  3567. Routine Description:
  3568. Save the state of a single outbound partner in the database.
  3569. Arguments:
  3570. ThreadCtx -- A Thread context to use for dbid and sesid.
  3571. Replica -- The replica set struct for the outbound log.
  3572. TableCtx -- ptr to the OutLogTable ctx.
  3573. OutLogPartner -- ptr to struct with current partner state.
  3574. Return Value:
  3575. Frs Status
  3576. --*/
  3577. {
  3578. #undef DEBSUB
  3579. #define DEBSUB "OutLogSaveSinglePartnerState:"
  3580. JET_ERR jerr, jerr1;
  3581. ULONG FStatus;
  3582. PCXTION_RECORD CxtionRecord = TableCtx->pDataRecord;
  3583. GUID *CxtionGuid;
  3584. CxtionRecord->Flags = OutLogPartner->Flags;
  3585. CxtionRecord->COLx = OutLogPartner->COLx;
  3586. CxtionRecord->COTx = OutLogPartner->COTx;
  3587. CxtionRecord->COTxNormalModeSave = OutLogPartner->COTxNormalModeSave;
  3588. CxtionRecord->COTslot = OutLogPartner->COTslot;
  3589. CopyMemory(CxtionRecord->AckVector, OutLogPartner->AckVector, ACK_VECTOR_BYTES);
  3590. //
  3591. // Seek to the record using the connection GUID.
  3592. //
  3593. CxtionGuid = OutLogPartner->Cxtion->Name->Guid;
  3594. jerr = DbsSeekRecord(ThreadCtx, CxtionGuid, CrCxtionGuidx, TableCtx);
  3595. if (!JET_SUCCESS(jerr)) {
  3596. DPRINT1_JS(0, "ERROR Seeking %ws\\%ws\\%ws -> %ws\\%ws :",
  3597. PRINT_CXTION_PATH(Replica, OutLogPartner->Cxtion), jerr);
  3598. return DbsTranslateJetError(jerr, FALSE);
  3599. }
  3600. //
  3601. // Save the record fields.
  3602. //
  3603. FStatus = DbsWriteTableFieldMult(ThreadCtx,
  3604. Replica->ReplicaNumber,
  3605. TableCtx,
  3606. OutLogUpdateFieldList,
  3607. ARRAY_SZ(OutLogUpdateFieldList));
  3608. DPRINT1_FS(0, "ERROR - OutLogSaveSinglePartnerState on %ws:", Replica->ReplicaName->Name, FStatus);
  3609. OutLogPartner->COTxLastSaved = OutLogPartner->COTx;
  3610. return FStatus;
  3611. }
  3612. ULONG
  3613. OutLogSavePartnerState(
  3614. IN PTHREAD_CTX ThreadCtx,
  3615. IN PREPLICA Replica,
  3616. IN PSINGLE_LIST_ENTRY CommitList,
  3617. IN PSINGLE_LIST_ENTRY EvalList
  3618. )
  3619. /*++
  3620. Routine Description:
  3621. Update the outbound log state for each partner on the CommitList.
  3622. Then for each partner on the EvalList update the state only if the
  3623. last saved Change Order Trailing index (COTxLastSaved) is less than
  3624. the new joint trailing index that was computed before we were called.
  3625. This is necessary because our caller is about to clean the OutBound log
  3626. up to the new JTx point so those records will be gone from the table.
  3627. If we crash we want COTx to be >= the JTx delete point.
  3628. Arguments:
  3629. ThreadCtx -- A Thread context to use for dbid and sesid.
  3630. Replica -- The replica set struct for the outbound log.
  3631. CommitList -- Ptr to list head of Outlog Partners that need state saved.
  3632. EvalList -- Ptr to list head of OutLog partners than need to be evaluated
  3633. for state save. They most move up to at least the new Joint
  3634. Trailing Index for the Replica.
  3635. Return Value:
  3636. Frs Status
  3637. --*/
  3638. {
  3639. #undef DEBSUB
  3640. #define DEBSUB "OutLogSavePartnerState:"
  3641. JET_ERR jerr, jerr1;
  3642. ULONG FStatus = FrsErrorSuccess;
  3643. TABLE_CTX TempTableCtx;
  3644. PTABLE_CTX TableCtx = &TempTableCtx;
  3645. POUT_LOG_PARTNER OutLogPartner;
  3646. PCXTION_RECORD CxtionRecord;
  3647. GUID *CxtionGuid;
  3648. TableCtx->TableType = TABLE_TYPE_INVALID;
  3649. TableCtx->Tid = JET_tableidNil;
  3650. //
  3651. // Open the connection table for this replica.
  3652. //
  3653. jerr = DbsOpenTable(ThreadCtx, TableCtx, Replica->ReplicaNumber, CXTIONTablex, NULL);
  3654. if (!JET_SUCCESS(jerr)) {
  3655. DPRINT1_JS(0, "DbsOpenTable (cxtion) on replica number %d failed.",
  3656. Replica->ReplicaNumber, jerr);
  3657. FStatus = DbsTranslateJetError(jerr, FALSE);
  3658. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  3659. DbsFreeTableCtx(TableCtx, 1);
  3660. return FStatus;
  3661. }
  3662. CxtionRecord = TableCtx->pDataRecord;
  3663. //
  3664. // Update the state of every partner on the commit list. This ensures that
  3665. // partners that are active will have their state updated even if an inactive
  3666. // partner is preventing the JTx from advancing.
  3667. //
  3668. ForEachSingleListEntry( CommitList, OUT_LOG_PARTNER, SaveList,
  3669. //
  3670. // Iterator pE is of type POUT_LOG_PARTNER.
  3671. //
  3672. OutLogSaveSinglePartnerState(ThreadCtx, Replica, TableCtx, pE);
  3673. );
  3674. //
  3675. // Check the COTxLastSaved of each partner on the Eval list.
  3676. // If it is Less than the new JointTrailing Index then update its state too.
  3677. //
  3678. ForEachSingleListEntry( EvalList, OUT_LOG_PARTNER, SaveList,
  3679. //
  3680. // Iterator pE is of type POUT_LOG_PARTNER.
  3681. //
  3682. if (pE->COTxLastSaved < Replica->OutLogJTx) {
  3683. OutLogSaveSinglePartnerState(ThreadCtx, Replica, TableCtx, pE);
  3684. }
  3685. );
  3686. //
  3687. // Close the table, reset the TableCtx Tid and Sesid.
  3688. // DbsCloseTable is a Macro, writes 1st arg.
  3689. //
  3690. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  3691. if (!JET_SUCCESS(jerr)) {
  3692. DPRINT_JS(0,"ERROR - JetCloseTable on OutLogSavePartnerState failed:", jerr);
  3693. FStatus = DbsTranslateJetError(jerr, FALSE);
  3694. }
  3695. DbsFreeTableCtx(TableCtx, 1);
  3696. return FStatus;
  3697. }
  3698. ULONG
  3699. OutLogPartnerVVJoinStart(
  3700. IN PTHREAD_CTX ThreadCtx,
  3701. IN PREPLICA Replica,
  3702. IN POUT_LOG_PARTNER OutLogPartner
  3703. )
  3704. /*++
  3705. Routine Description:
  3706. This outbound partner has just entered VVJoin mode.
  3707. Save the current Outlog sequence number for continuation when the
  3708. partner leaves VVJoin Mode. Reset the ACK vector and update the
  3709. partner state in the database so if the VVJoin is interrupted we can
  3710. restart it.
  3711. Note: The caller must get the Outlog lock.
  3712. Arguments:
  3713. ThreadCtx -- A Thread context to use for dbid and sesid.
  3714. Replica -- The replica set struct for the outbound log.
  3715. OutLogPartner -- ptr to struct with current partner state.
  3716. Return Value:
  3717. Frs Status
  3718. --*/
  3719. {
  3720. #undef DEBSUB
  3721. #define DEBSUB "OutLogPartnerVVJoinStart:"
  3722. JET_ERR jerr, jerr1;
  3723. ULONG FStatus;
  3724. TABLE_CTX TempTableCtx;
  3725. PTABLE_CTX TableCtx = &TempTableCtx;
  3726. FRS_ASSERT(!InVVJoinMode(OutLogPartner));
  3727. //
  3728. // Save the current OutLog insertion point as the restart point when
  3729. // the partner returns to normal mode.
  3730. //
  3731. if (OutLogPartner->COTxNormalModeSave == 0) {
  3732. OutLogPartner->COTxNormalModeSave = Replica->OutLogSeqNumber;
  3733. }
  3734. //
  3735. // Advance the leading and trailing outlog indexes to the end of the outlog.
  3736. //
  3737. OutLogPartner->COLxVVJoinDone = 0;
  3738. OutLogPartner->COLxRestart = 0;
  3739. OutLogPartner->COLx = Replica->OutLogSeqNumber;
  3740. OutLogPartner->COTx = Replica->OutLogSeqNumber;
  3741. OutLogPartner->COTxLastSaved = OutLogPartner->COTxNormalModeSave;
  3742. //
  3743. // For a partner entering VVJoin Mode I would expect the outstanding CO
  3744. // count to be zero. But it might not be.
  3745. //
  3746. if (OutLogPartner->OutstandingCos > 0) {
  3747. DPRINT1(0, "WARNING: OutstandingCos is %d. setting to zero.\n", OutLogPartner->OutstandingCos);
  3748. }
  3749. //
  3750. // Reset the Ack Vector and start with a fresh count of outstanding COs.
  3751. //
  3752. ResetAckVector(OutLogPartner);
  3753. //
  3754. // Enable VVJoin Mode.
  3755. //
  3756. SetFlag(OutLogPartner->Flags, OLP_FLAGS_VVJOIN_MODE);
  3757. //
  3758. // Make sure we are not in replay mode.
  3759. //
  3760. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_REPLAY_MODE);
  3761. //
  3762. // Make sure we are not in opt vvjoin mode.
  3763. //
  3764. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_OPT_VVJOIN_MODE);
  3765. //
  3766. // Open the connection table and update the partner state now that
  3767. // we are entering VVJoin Mode.
  3768. //
  3769. TableCtx->TableType = TABLE_TYPE_INVALID;
  3770. TableCtx->Tid = JET_tableidNil;
  3771. jerr = DbsOpenTable(ThreadCtx, TableCtx, Replica->ReplicaNumber, CXTIONTablex, NULL);
  3772. if (!JET_SUCCESS(jerr)) {
  3773. DPRINT1_JS(0, "DbsOpenTable (cxtion) on replica number %d failed.",
  3774. Replica->ReplicaNumber, jerr);
  3775. FStatus = DbsTranslateJetError(jerr, FALSE);
  3776. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  3777. DbsFreeTableCtx(TableCtx, 1);
  3778. return FStatus;
  3779. }
  3780. OutLogSaveSinglePartnerState(ThreadCtx, Replica, TableCtx, OutLogPartner);
  3781. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  3782. DbsFreeTableCtx(TableCtx, 1);
  3783. FRS_PRINT_TYPE(4, OutLogPartner);
  3784. //
  3785. // Compare the version vectors with the idtable and generate change orders
  3786. //
  3787. SubmitVvJoin(Replica, OutLogPartner->Cxtion, CMD_VVJOIN_START);
  3788. return FrsErrorSuccess;
  3789. }
  3790. ULONG
  3791. OutLogPartnerVVJoinDone(
  3792. IN PTHREAD_CTX ThreadCtx,
  3793. IN PREPLICA Replica,
  3794. IN POUT_LOG_PARTNER OutLogPartner
  3795. )
  3796. /*++
  3797. Routine Description:
  3798. This outbound partner is now leaving VVJoin mode.
  3799. Restore the saved Outlog sequence number so we now send out any
  3800. normal mode change orders that were held up while the VVJoin was going on.
  3801. Reset the ACK vector and update the partner state in the database
  3802. so we know we have left VVJoin mode if the system crashes.
  3803. Note: The caller must get the Outlog lock.
  3804. Arguments:
  3805. ThreadCtx -- A Thread context to use for dbid and sesid.
  3806. Replica -- The replica set struct for the outbound log.
  3807. OutLogPartner -- ptr to struct with current partner state.
  3808. Return Value:
  3809. Frs Status
  3810. --*/
  3811. {
  3812. #undef DEBSUB
  3813. #define DEBSUB "OutLogPartnerVVJoinDone:"
  3814. JET_ERR jerr, jerr1;
  3815. ULONG FStatus;
  3816. TABLE_CTX TempTableCtx;
  3817. PTABLE_CTX TableCtx = &TempTableCtx;
  3818. FRS_ASSERT(InVVJoinMode(OutLogPartner));
  3819. //
  3820. // Restore the OutLog restart point for this partner.
  3821. //
  3822. OutLogPartner->COLxVVJoinDone = OutLogPartner->COLx;
  3823. OutLogPartner->COLxRestart = OutLogPartner->COTxNormalModeSave;
  3824. OutLogPartner->COLx = OutLogPartner->COTxNormalModeSave;
  3825. OutLogPartner->COTx = OutLogPartner->COTxNormalModeSave;
  3826. OutLogPartner->COTxLastSaved = OutLogPartner->COTxNormalModeSave;
  3827. OutLogPartner->COTxNormalModeSave = 0;
  3828. //
  3829. // Reset the Ack Vector and start with a fresh count of outstanding COs.
  3830. //
  3831. ResetAckVector(OutLogPartner);
  3832. //
  3833. // Leave VVJoin Mode.
  3834. //
  3835. ClearFlag(OutLogPartner->Flags, OLP_FLAGS_VVJOIN_MODE);
  3836. //
  3837. // Enter Replay Mode.
  3838. //
  3839. SetFlag(OutLogPartner->Flags, OLP_FLAGS_REPLAY_MODE);
  3840. //
  3841. // Open the connection table and update the partner state.
  3842. //
  3843. TableCtx->TableType = TABLE_TYPE_INVALID;
  3844. TableCtx->Tid = JET_tableidNil;
  3845. jerr = DbsOpenTable(ThreadCtx, TableCtx, Replica->ReplicaNumber, CXTIONTablex, NULL);
  3846. if (!JET_SUCCESS(jerr)) {
  3847. DPRINT1_JS(0, "DbsOpenTable (cxtion) on replica number %d failed.",
  3848. Replica->ReplicaNumber, jerr);
  3849. FStatus = DbsTranslateJetError(jerr, FALSE);
  3850. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  3851. DbsFreeTableCtx(TableCtx, 1);
  3852. return FStatus;
  3853. }
  3854. OutLogSaveSinglePartnerState(ThreadCtx, Replica, TableCtx, OutLogPartner);
  3855. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  3856. DbsFreeTableCtx(TableCtx, 1);
  3857. FRS_PRINT_TYPE(4, OutLogPartner);
  3858. return FrsErrorSuccess;
  3859. }
  3860. JET_ERR
  3861. OutLogCleanupWorker(
  3862. IN PTHREAD_CTX ThreadCtx,
  3863. IN PTABLE_CTX OutLogTableCtx,
  3864. IN PCHANGE_ORDER_COMMAND CoCmd,
  3865. IN PVOID Context,
  3866. IN ULONG OutLogSeqNumber
  3867. )
  3868. /*++
  3869. Routine Description:
  3870. This is a worker function passed to DbsEnumerateOutlogTable().
  3871. Each time it is called it may delete the record from the table
  3872. and/or delete the corresponding staging file.
  3873. Arguments:
  3874. ThreadCtx - Needed to access Jet.
  3875. OutLogTableCtx - A ptr to an outbound log context struct.
  3876. CoCmd - A ptr to a change order command record. (NULL if record
  3877. was deleted)
  3878. Context - A ptr to the Replica struct we are cleaning up.
  3879. OutLogSeqNumber - The sequence number of this record.
  3880. Thread Return Value:
  3881. JET_errSuccess
  3882. --*/
  3883. {
  3884. #undef DEBSUB
  3885. #define DEBSUB "OutLogCleanupWorker:"
  3886. JET_ERR jerr, jerr1;
  3887. ULONG FStatus;
  3888. POUTLOG_TABLE_ENUM pOutlogTableEnum = (POUTLOG_TABLE_ENUM) Context;
  3889. PREPLICA Replica = pOutlogTableEnum->Replica;
  3890. ULONG JointTrailingIndex = Replica->OutLogJTx;
  3891. BOOL DirectedCo;
  3892. PCXTION Cxtion;
  3893. PVOID Key;
  3894. PTABLE_CTX VVTableCtx = &pOutlogTableEnum->VVTableCtx;
  3895. PVVTABLE_RECORD VVTableRec;
  3896. PDATA_EXTENSION_RETRY_TIMEOUT CoCmdRetryTimeout;
  3897. LONGLONG CurrentTime;
  3898. LONGLONG TimeDelta;
  3899. PVV_ENTRY OutlogVVEntry;
  3900. PVV_ENTRY VVEntry;
  3901. ULONGLONG VVEntryVsn;
  3902. BOOL DeleteFromOutlog = FALSE;
  3903. FRS_ASSERT(VVTableCtx->pDataRecord != NULL);
  3904. //
  3905. // Abort cleanup if service is shutting down.
  3906. //
  3907. if (FrsIsShuttingDown) {
  3908. return JET_errTermInProgress;
  3909. }
  3910. //
  3911. // If this record has already been deleted then continue enumeration.
  3912. //
  3913. if (CoCmd == NULL) {
  3914. return JET_errSuccess;
  3915. }
  3916. //
  3917. // If the local install is not done then don't delete the staging file.
  3918. //
  3919. if (BooleanFlagOn(CoCmd->Flags, CO_FLAG_INSTALL_INCOMPLETE)) {
  3920. DPRINT2(4, "Install Incomplete for Index 0x%x, File: %ws\n",
  3921. OutLogSeqNumber, CoCmd->FileName);
  3922. return JET_errSuccess;
  3923. }
  3924. DirectedCo = BooleanFlagOn(CoCmd->Flags, CO_FLAG_DIRECTED_CO);
  3925. if (!DirectedCo) {
  3926. //
  3927. // Check if it is time to delete this CO from the outbound log.
  3928. // The outlog insert time of the CO is stored in the extension field.
  3929. //
  3930. DeleteFromOutlog = FALSE;
  3931. CoCmdRetryTimeout = DbsDataExtensionFind(CoCmd->Extension, DataExtend_Retry_Timeout);
  3932. if(CoCmdRetryTimeout != NULL) {
  3933. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  3934. TimeDelta = CurrentTime - CoCmdRetryTimeout->FirstTryTime;
  3935. TimeDelta = TimeDelta / CONVERT_FILETIME_TO_MINUTES;
  3936. if (TimeDelta > DebugInfo.OutlogChangeHistory) {
  3937. DeleteFromOutlog = TRUE;
  3938. //
  3939. // This is a Normal CO so check it against the JointTrailingIndex to
  3940. // see if it can be deleted. Note that the JTX has been held back
  3941. // for those partners in VV Join Mode so they will get these COs when
  3942. // they are done with VVJoin Mode.
  3943. //
  3944. if (OutLogSeqNumber >= JointTrailingIndex) {
  3945. //
  3946. // Find the connection that is holding us back.
  3947. //
  3948. Key = NULL;
  3949. while (Cxtion = GTabNextDatum(Replica->Cxtions, &Key)) {
  3950. if (Cxtion->JrnlCxtion || Cxtion->Inbound) {
  3951. continue;
  3952. }
  3953. if (Cxtion->OLCtx == NULL) {
  3954. continue;
  3955. }
  3956. if (InVVJoinMode(Cxtion->OLCtx)) {
  3957. if (OutLogSeqNumber >= Cxtion->OLCtx->COTxNormalModeSave) {
  3958. SetCxtionFlag(Cxtion, CXTION_FLAGS_TRIM_OUTLOG);
  3959. DPRINT1(0, "Deleting connection in vvjoin mode %ws\n", Cxtion->Name->Name);
  3960. }
  3961. } else if (OutLogSeqNumber >= Cxtion->OLCtx->COTx) {
  3962. SetCxtionFlag(Cxtion, CXTION_FLAGS_TRIM_OUTLOG);
  3963. DPRINT1(0, "Deleting connection %ws\n", Cxtion->Name->Name);
  3964. }
  3965. }
  3966. FrsDsSetDsPollingInterval(1,0,0);
  3967. return JET_errRecordNotFound;
  3968. }
  3969. }
  3970. }
  3971. //
  3972. // Terminate the enumeration early in both of the following
  3973. // cases.
  3974. // No VVJoins going on AND:
  3975. //
  3976. // 1. We have caught up with the JointTrailingIndex.
  3977. // (OutLogSeqNumber >= JointTrailingIndex)
  3978. //
  3979. // 2. There are no outbound connections or there are
  3980. // outbound connections but they have never joined.
  3981. // (JointTrailingIndex == 0xFFFFFFFF)
  3982. // Also the CO has not lived in the outlog past the
  3983. // Outlog Change History.
  3984. // (DeleteFromOutlog == FALSE)
  3985. //
  3986. if (((DeleteFromOutlog == FALSE) &&
  3987. (JointTrailingIndex == 0xFFFFFFFF)) ||
  3988. (OutLogSeqNumber >= JointTrailingIndex)) {
  3989. if (Replica->OutLogCountVVJoins != 0) {
  3990. //
  3991. // Since we have VVJoins active just skip this CO and keep looking
  3992. // for directed COs that can be deleted.
  3993. //
  3994. return JET_errSuccess;
  3995. } else {
  3996. //
  3997. // No VVJoins active so we can stop this cleanup pass.
  3998. //
  3999. return JET_errRecordNotFound;
  4000. }
  4001. }
  4002. } else {
  4003. //
  4004. // This is a directed CO. It may be VVJoin Related or just a partner
  4005. // initiated refresh request. It is directed to a single outbound
  4006. // partner. Either way test the sequence number against
  4007. // the specified partner's current COTx to decide deletion.
  4008. //
  4009. Cxtion = GTabLookup(Replica->Cxtions, &CoCmd->CxtionGuid, NULL);
  4010. //
  4011. // If we don't find the connection then it is deleted so delete the
  4012. // change order.
  4013. //
  4014. if (Cxtion != NULL) {
  4015. FRS_ASSERT(!Cxtion->Inbound);
  4016. FRS_ASSERT(Cxtion->OLCtx != NULL);
  4017. //
  4018. // Check the sequence number against the current trailing index on
  4019. // this connection. This works regardless of the connection being
  4020. // in Join Mode since the current value is still correct.
  4021. //
  4022. if (OutLogSeqNumber >= Cxtion->OLCtx->COTx) {
  4023. return JET_errSuccess;
  4024. }
  4025. }
  4026. }
  4027. //
  4028. // We do not want to try to delete staging file every time we scan the outbound log.
  4029. //
  4030. if ((OutLogSeqNumber < pOutlogTableEnum->OldJointTrailingIndex) && !DirectedCo && (DeleteFromOutlog == FALSE)) {
  4031. return JET_errSuccess;
  4032. }
  4033. DPRINT2(4, "Deleting Staging file for Index 0x%x, File: %ws\n",
  4034. OutLogSeqNumber, CoCmd->FileName);
  4035. //
  4036. // Delete the staging file and then the log record.
  4037. //
  4038. if (StageDeleteFile(CoCmd, NULL, TRUE)) {
  4039. //
  4040. // Now delete the outlog record. If we fail to delete the staging
  4041. // file for some reason the outlog record will stay around so
  4042. // we can try next time. If there is a problem, complain but keep
  4043. // going.
  4044. //
  4045. if (!DirectedCo) {
  4046. if ((DebugInfo.SaveOutlogChangeHistory == FALSE) || (DeleteFromOutlog == TRUE)) {
  4047. OutlogVVEntry = GTabLookupNoLock(Replica->OutlogVVector, &CoCmd->OriginatorGuid, NULL);
  4048. //
  4049. // The CO can be in the outbound log but has not yet updated the
  4050. // vv in the DB.
  4051. //
  4052. if (OutlogVVEntry == NULL) {
  4053. OutlogVVEntry = FrsAlloc(sizeof(VV_ENTRY));
  4054. COPY_GUID(&OutlogVVEntry->GVsn.Guid, &CoCmd->OriginatorGuid);
  4055. OutlogVVEntry->GVsn.Vsn = (ULONGLONG)1;
  4056. //
  4057. // Initialize the list head. We do not use the list head for outlogVV
  4058. // but we still need to initialize it because the APIs that work with
  4059. // VVs expect it.
  4060. //
  4061. InitializeListHead(&OutlogVVEntry->ListHead);
  4062. //
  4063. // Add it to the outlog version vector table.
  4064. //
  4065. GTabInsertEntryNoLock(Replica->OutlogVVector, OutlogVVEntry, &OutlogVVEntry->GVsn.Guid, NULL);
  4066. }
  4067. DPRINT1(4, "CoCmd->FrsVsn : %08x %08x\n",
  4068. PRINTQUAD(CoCmd->FrsVsn));
  4069. DPRINT1(4, "VVEntry->GVsn.Vsn : %08x %08x\n",
  4070. PRINTQUAD(OutlogVVEntry->GVsn.Vsn));
  4071. if (CoCmd->FrsVsn > OutlogVVEntry->GVsn.Vsn) {
  4072. OutlogVVEntry->GVsn.Vsn = CoCmd->FrsVsn;
  4073. jerr = DbsSeekRecord(ThreadCtx, &CoCmd->OriginatorGuid, VVOriginatorGuidIndexx, VVTableCtx);
  4074. VVTableRec = (PVVTABLE_RECORD)VVTableCtx->pDataRecord;
  4075. if (JET_SUCCESS(jerr)) {
  4076. DPRINT2(4, "VVTableRec->VVOriginatorVsn %08x %08x \
  4077. VVTableRec->VVOutlogOriginatorVsn %08x %08x\n",
  4078. PRINTQUAD(VVTableRec->VVOriginatorVsn),
  4079. PRINTQUAD(VVTableRec->VVOutlogOriginatorVsn));
  4080. VVTableRec->VVOutlogOriginatorVsn = OutlogVVEntry->GVsn.Vsn;
  4081. //
  4082. // Write the desired field.
  4083. //
  4084. FStatus = DbsWriteTableField(ThreadCtx, Replica->ReplicaNumber, VVTableCtx, VVOutlogOriginatorVsnx);
  4085. DPRINT1_FS(0, "++ ERROR - DbsWriteTableField on %ws :", Replica->ReplicaName->Name, FStatus);
  4086. } else {
  4087. DPRINT1_JS(0, "++ ERROR - DbsSeekRecord on %ws :", Replica->ReplicaName->Name, jerr);
  4088. COPY_GUID(&VVTableRec->VVOriginatorGuid, &OutlogVVEntry->GVsn.Guid);
  4089. VVTableRec->VVOriginatorVsn = 1;
  4090. VVTableRec->VVOutlogOriginatorVsn = OutlogVVEntry->GVsn.Vsn;
  4091. FStatus = DbsInsertTable2(VVTableCtx);
  4092. if (!FRS_SUCCESS(FStatus)) {
  4093. return JET_errRecordNotFound;
  4094. }
  4095. }
  4096. }
  4097. //
  4098. // Update the start point of the outbound log
  4099. // We increment OutLogCOMin by 1. That could be pointing to a deleted record
  4100. // but that should not matter as we skip deleted records.
  4101. //
  4102. Replica->OutLogCOMin = CoCmd->SequenceNumber + 1;
  4103. } else {
  4104. return JET_errSuccess;
  4105. }
  4106. }
  4107. DPRINT2(4, "Deleting Outlog record for Index 0x%x, File: %ws\n",
  4108. OutLogSeqNumber, CoCmd->FileName);
  4109. jerr = DbsDeleteTableRecord(OutLogTableCtx);
  4110. DPRINT_JS(0, "ERROR - DbsDeleteTableRecord :", jerr);
  4111. }
  4112. //
  4113. // Return success until we hit the Joint Trailing Index.
  4114. //
  4115. return JET_errSuccess;
  4116. }
  4117. ULONG
  4118. OutLogCleanupDominantFileTableWorker (
  4119. PQHASH_TABLE Table,
  4120. PQHASH_ENTRY BeforeNode,
  4121. PQHASH_ENTRY TargetNode,
  4122. PVOID Context
  4123. )
  4124. /*++
  4125. Routine Description:
  4126. This function is called thru QHashEnumerateTable() to remove entries
  4127. that have no multiples.
  4128. Arguments:
  4129. Table - the hash table being enumerated
  4130. BeforeNode -- ptr to the QhashEntry before the node of interest.
  4131. TargetNode -- ptr to the QhashEntry of interest.
  4132. Context - Replica ptr.
  4133. Return Value:
  4134. Win32 status
  4135. --*/
  4136. {
  4137. #undef DEBSUB
  4138. #define DEBSUB "OutLogCleanupDominantFileTableWorker:"
  4139. PREPLICA Replica = (PREPLICA) Context;
  4140. PDOMINANT_FILE_ENTRY DomFileEntry;
  4141. DomFileEntry = (PDOMINANT_FILE_ENTRY) (TargetNode->Flags);
  4142. //
  4143. // If the OutlogCoMin has passed this entry then delete it.
  4144. //
  4145. if (DomFileEntry->OLSeqNum <= Replica->OutLogCOMin) {
  4146. FrsFree(DomFileEntry);
  4147. TargetNode->Flags = 0;
  4148. //
  4149. // Tell QHashEnumerateTable() to delete the QHash node and continue the enum.
  4150. //
  4151. return FrsErrorDeleteRequested;
  4152. }
  4153. return FrsErrorSuccess;
  4154. }
  4155. VOID
  4156. OutLogJointTrailingIndexMerge(
  4157. POUT_LOG_PARTNER Olp,
  4158. PREPLICA Replica,
  4159. PULONG JointTrailingIndex
  4160. )
  4161. /*++
  4162. Routine Description:
  4163. Combine outlog partner info to form new joint trailing index.
  4164. Count the number of outlog partners in VV Join Mode.
  4165. Note: The caller has acquired the outbound log lock.
  4166. Arguments:
  4167. ThreadCtx -- A Thread context to use for dbid and sesid.
  4168. Replica -- The replica set struct for the outbound log.
  4169. JointTrailingIndex -- new value returned for JointTrailingIndex.
  4170. Return Value:
  4171. None
  4172. --*/
  4173. {
  4174. #undef DEBSUB
  4175. #define DEBSUB "OutLogJointTrailingIndexMerge:"
  4176. ULONG CleanPoint;
  4177. //
  4178. // Current clean point for this partner is the CO Trailing Index.
  4179. //
  4180. CleanPoint = Olp->COTx;
  4181. //
  4182. // Unless partner is in VV Join Mode in which the clean point was saved
  4183. // in COTxNormalModeSave before entering Join Mode.
  4184. // Count the number of outlog partners in VVJoin Mode.
  4185. //
  4186. if (InVVJoinMode(Olp)) {
  4187. CleanPoint = Olp->COTxNormalModeSave;
  4188. Replica->OutLogCountVVJoins += 1;
  4189. }
  4190. //
  4191. // If this clean point is less than the current JointTrailingIndex then
  4192. // move the JointTrailingIndex back. A zero clean point means this partner
  4193. // has never joined so we will force him to do a VVJoin the first time
  4194. // it joins. Meanwhile we don't take up log or staging file space if
  4195. // he never joins.
  4196. //
  4197. if ((CleanPoint != 0) && (CleanPoint < *JointTrailingIndex)) {
  4198. *JointTrailingIndex = CleanPoint;
  4199. }
  4200. }
  4201. ULONG
  4202. OutLogCleanupLog(
  4203. PTHREAD_CTX ThreadCtx,
  4204. PREPLICA Replica
  4205. )
  4206. /*++
  4207. Routine Description:
  4208. It's time to remove the outbound log change orders that have been
  4209. sent to all the partners for this Replica. This is done by calculating
  4210. the Joint Trailing Index across all partners and then deleting the
  4211. records up to that point. When the record is deleted the staging file
  4212. is also deleted.
  4213. WARNING: It is only safe to call this function after all outbound
  4214. connections have been initialized. They don't have to be active but
  4215. we need to have loaded up their trailing index state into their
  4216. OUT_LOG_PARTNER struct.
  4217. Arguments:
  4218. ThreadCtx -- A Thread context to use for dbid and sesid.
  4219. Replica -- The replica set struct for the outbound log.
  4220. Return Value:
  4221. Frs Status
  4222. --*/
  4223. {
  4224. #undef DEBSUB
  4225. #define DEBSUB "OutLogCleanupLog:"
  4226. JET_ERR jerr, jerr1;
  4227. ULONG FStatus;
  4228. ULONG JointTrailingIndex = 0xFFFFFFFF;
  4229. ULONG OldJointTrailingIndex;
  4230. OUTLOG_TABLE_ENUM OutlogTableEnum;
  4231. TABLE_CTX TempTableCtx;
  4232. PTABLE_CTX TableCtx = &TempTableCtx;
  4233. SINGLE_LIST_ENTRY CommitList, EvalList;
  4234. //
  4235. // Get the outbound log lock for this replica and hold it until we
  4236. // are finished.
  4237. //
  4238. OutLogAcquireLock(Replica);
  4239. LOCK_GEN_TABLE(Replica->OutlogVVector);
  4240. Replica->OutLogDoCleanup = FALSE;
  4241. Replica->OutLogCountVVJoins = 0;
  4242. CommitList.Next = NULL;
  4243. EvalList.Next = NULL;
  4244. //
  4245. // Find the Joint Trailing Index across all partners regardless of their
  4246. // current connected/unconnected state. Iterator pE is type *OUT_LOG_PARTNER
  4247. // Also count the number of outlog partners in VV Join Mode.
  4248. //
  4249. ForEachSimpleListEntry(&Replica->OutLogEligible, OUT_LOG_PARTNER, List,
  4250. OutLogJointTrailingIndexMerge(pE, Replica, &JointTrailingIndex);
  4251. OUT_LOG_TRACK_PARTNER_STATE_UPDATE(pE, &CommitList, &EvalList);
  4252. );
  4253. ForEachSimpleListEntry(&Replica->OutLogStandBy, OUT_LOG_PARTNER, List,
  4254. OutLogJointTrailingIndexMerge(pE, Replica, &JointTrailingIndex);
  4255. OUT_LOG_TRACK_PARTNER_STATE_UPDATE(pE, &CommitList, &EvalList);
  4256. );
  4257. ForEachSimpleListEntry(&Replica->OutLogActive, OUT_LOG_PARTNER, List,
  4258. OutLogJointTrailingIndexMerge(pE, Replica, &JointTrailingIndex);
  4259. OUT_LOG_TRACK_PARTNER_STATE_UPDATE(pE, &CommitList, &EvalList);
  4260. );
  4261. ForEachSimpleListEntry(&Replica->OutLogInActive, OUT_LOG_PARTNER, List,
  4262. OutLogJointTrailingIndexMerge(pE, Replica, &JointTrailingIndex);
  4263. );
  4264. DPRINT1(4, "Old JointTrailingIndex = 0x%x\n", Replica->OutLogJTx);
  4265. DPRINT1(4, "New JointTrailingIndex = 0x%x\n", JointTrailingIndex);
  4266. DPRINT1(4, "Count of OutLog Partners in VVJoin Mode = %d\n",
  4267. Replica->OutLogCountVVJoins);
  4268. OldJointTrailingIndex = Replica->OutLogJTx;
  4269. Replica->OutLogJTx = JointTrailingIndex;
  4270. OutLogSavePartnerState(ThreadCtx, Replica, &CommitList, &EvalList);
  4271. OutLogReleaseLock(Replica);
  4272. //
  4273. // Clear out the Dominant File Table.up to the JointTrailingIndex.
  4274. //
  4275. QHashEnumerateTable(Replica->OutLogDominantTable,
  4276. OutLogCleanupDominantFileTableWorker,
  4277. Replica);
  4278. //
  4279. // Walk through the outbound log up to the JointTrailingIndex (passed
  4280. // through the Replica struct) and delete each record and the staging file.
  4281. // Init the table ctx and then open the outbound log table for this Replica.
  4282. //
  4283. TableCtx->TableType = TABLE_TYPE_INVALID;
  4284. TableCtx->Tid = JET_tableidNil;
  4285. jerr = DbsOpenTable(ThreadCtx, TableCtx, Replica->ReplicaNumber, OUTLOGTablex, NULL);
  4286. if (!JET_SUCCESS(jerr)) {
  4287. DPRINT1_JS(0, "DbsOpenTable (outlog) on replica number %d failed.",
  4288. Replica->ReplicaNumber, jerr);
  4289. FStatus = DbsTranslateJetError(jerr, FALSE);
  4290. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  4291. DbsFreeTableCtx(TableCtx, 1);
  4292. UNLOCK_GEN_TABLE(Replica->OutlogVVector);
  4293. return FStatus;
  4294. }
  4295. OutlogTableEnum.Replica = Replica;
  4296. OutlogTableEnum.OldJointTrailingIndex = OldJointTrailingIndex;
  4297. // Init the table ctx.
  4298. OutlogTableEnum.VVTableCtx.TableType = TABLE_TYPE_INVALID;
  4299. OutlogTableEnum.VVTableCtx.Tid = JET_tableidNil;
  4300. //
  4301. // Open the VVTable and pass the table context to the worker function.
  4302. //
  4303. jerr = DbsOpenTable(ThreadCtx,&OutlogTableEnum.VVTableCtx,Replica->ReplicaNumber,VVTablex,NULL);
  4304. if (!JET_SUCCESS(jerr)) {
  4305. DPRINT1_JS(0, "DbsOpenTable (vvtable) on replica number %d failed.",
  4306. Replica->ReplicaNumber, jerr);
  4307. FStatus = DbsTranslateJetError(jerr, FALSE);
  4308. // Close and free VVTable context
  4309. DbsCloseTable(jerr, ThreadCtx->JSesid, (&OutlogTableEnum.VVTableCtx));
  4310. DbsFreeTableCtx(&OutlogTableEnum.VVTableCtx, 1);
  4311. // Close and free Outlog table context.
  4312. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  4313. DbsFreeTableCtx(TableCtx, 1);
  4314. UNLOCK_GEN_TABLE(Replica->OutlogVVector);
  4315. return FStatus;
  4316. }
  4317. jerr = DbsEnumerateOutlogTable(ThreadCtx,
  4318. TableCtx,
  4319. Replica->OutLogSeqNumber,
  4320. OutLogCleanupWorker,
  4321. &OutlogTableEnum);
  4322. if ((!JET_SUCCESS(jerr)) &&
  4323. (jerr != JET_errRecordNotFound) &&
  4324. (jerr != JET_errNoCurrentRecord)) {
  4325. DPRINT_JS(0, "ERROR - FrsEnumerateTable for OutLogCleanupWorker :", jerr);
  4326. }
  4327. // Close and free VVTable context
  4328. DbsCloseTable(jerr, ThreadCtx->JSesid, (&OutlogTableEnum.VVTableCtx));
  4329. DbsFreeTableCtx(&OutlogTableEnum.VVTableCtx, 1);
  4330. // Close and free Outlog table context.
  4331. DbsCloseTable(jerr, ThreadCtx->JSesid, TableCtx);
  4332. DbsFreeTableCtx(TableCtx, 1);
  4333. UNLOCK_GEN_TABLE(Replica->OutlogVVector);
  4334. return DbsTranslateJetError(jerr, FALSE);
  4335. }
  4336. ULONG
  4337. OutLogCleanup(
  4338. PTHREAD_CTX ThreadCtx,
  4339. PCOMMAND_PACKET Cmd
  4340. )
  4341. /*++
  4342. Routine Description:
  4343. connections have been initialized. They don't have to be active but
  4344. we need to have loaded up their trailing index state into their
  4345. OUT_LOG_PARTNER struct.
  4346. Arguments:
  4347. ThreadCtx -- A Thread context to use for dbid and sesid.
  4348. Cmd -- Command packet.
  4349. Return Value:
  4350. Frs Status
  4351. --*/
  4352. {
  4353. #undef DEBSUB
  4354. #define DEBSUB "OutLogCleanup:"
  4355. ULONGLONG StartTime, StopTime, qTimeDelta;
  4356. ULONG TimeDelta, TotalSets, i, ArrayLen, OutlogChangeHistoryInMs;
  4357. PREPLICA *CleanupArray, Replica;
  4358. GetSystemTimeAsFileTime((PFILETIME)&StartTime);
  4359. //
  4360. // Allocate an array to hold the ptrs to each active replica set.
  4361. //
  4362. ArrayLen = MaxNumberReplicaSets;
  4363. AquireListLock(&ReplicaListHead);
  4364. RETRY:
  4365. CleanupArray = FrsAlloc(ArrayLen * sizeof(PREPLICA));
  4366. TotalSets = 0;
  4367. ForEachListEntryLock( &ReplicaListHead, REPLICA, ReplicaList,
  4368. // Induction variable pE is of type PREPLICA.
  4369. CleanupArray[TotalSets++] = pE;
  4370. if (TotalSets >= ArrayLen) {
  4371. FrsFree(CleanupArray);
  4372. ArrayLen += MaxNumberReplicaSets;
  4373. FRS_ASSERT(ArrayLen < 1000000);
  4374. goto RETRY;
  4375. }
  4376. );
  4377. if (!LockedOutlogCleanup) {
  4378. ReleaseListLock(&ReplicaListHead);
  4379. }
  4380. //
  4381. // Do outbound log cleanup on each replica if something happened.
  4382. // We do not have the ReplicaList lock because cleanup can take many
  4383. // minutes and we will block other threads that need to access the list.
  4384. //
  4385. for (i=0; i<TotalSets; i++) {
  4386. Replica = CleanupArray[i];
  4387. DPRINT1(5, "LogCleanup on %ws\n", Replica->ReplicaName->Name);
  4388. if ((Replica->OutLogDoCleanup || (RsTimeout(Cmd) == 0)) && !FrsIsShuttingDown) {
  4389. DPRINT3(4, "OutLog Cleanup for replica %ws, id: %d, (%08x)\n",
  4390. Replica->ReplicaName->Name, Replica->ReplicaNumber, Replica);
  4391. OutLogCleanupLog(ThreadCtx, Replica);
  4392. }
  4393. }
  4394. GetSystemTimeAsFileTime((PFILETIME)&StopTime);
  4395. //
  4396. // get the time difference in millisec and scale by 50 so the time spent
  4397. // in cleanup is around 2% max. But not more frequently than OUT_LOG_CLEAN_INTERVAL
  4398. // and not less frequently than OUT_LOG_FORCE_CLEAN_INTERVAL.
  4399. //
  4400. qTimeDelta = StopTime - StartTime;
  4401. qTimeDelta = qTimeDelta / ((ULONGLONG)1000L * 10L);
  4402. TimeDelta = max((ULONG) qTimeDelta * 50, OUT_LOG_CLEAN_INTERVAL);
  4403. TimeDelta = min(TimeDelta, OUT_LOG_FORCE_CLEAN_INTERVAL);
  4404. //
  4405. // If changes are being saved in the outbound log then
  4406. // adjust the cleanup interval accordingly.
  4407. // If the Outlog Change History is set to 30 minutes
  4408. // we don't want to wait 8 hours for the cleanup to
  4409. // run.
  4410. //
  4411. // NOTE: DebugInfo.OutlogChangeHistory is in minutes, so we need to
  4412. // convert to milliseconds.
  4413. //
  4414. OutlogChangeHistoryInMs = DebugInfo.OutlogChangeHistory * 60 * 1000;
  4415. if (DebugInfo.SaveOutlogChangeHistory == TRUE) {
  4416. TimeDelta = min(TimeDelta, OutlogChangeHistoryInMs);
  4417. }
  4418. DPRINT2(4, "Time for cleanup (ms): %08x %08x Next Cleanup after: %d ms\n",
  4419. PRINTQUAD(qTimeDelta), TimeDelta);
  4420. //
  4421. // Force the timeout after attempting cleanup RsTimeout(Cmd) times.
  4422. //
  4423. if (RsTimeout(Cmd) == 0) {
  4424. if (DebugInfo.SaveOutlogChangeHistory == TRUE) {
  4425. RsTimeout(Cmd) = min(OUT_LOG_FORCE_CLEAN_INTERVAL, OutlogChangeHistoryInMs) - TimeDelta;
  4426. } else {
  4427. RsTimeout(Cmd) = OUT_LOG_FORCE_CLEAN_INTERVAL - TimeDelta;
  4428. }
  4429. } else
  4430. if (RsTimeout(Cmd) < TimeDelta) {
  4431. // RsTimeout is a ULONG, it cannot be negative!
  4432. RsTimeout(Cmd) = 0;
  4433. } else {
  4434. RsTimeout(Cmd) -= TimeDelta;
  4435. }
  4436. if (LockedOutlogCleanup) {
  4437. ReleaseListLock(&ReplicaListHead);
  4438. }
  4439. FrsFree(CleanupArray);
  4440. if (!FrsIsShuttingDown) {
  4441. FrsDelQueueSubmit(Cmd, TimeDelta);
  4442. return FrsErrorSuccess;
  4443. }
  4444. return FrsErrorShuttingDown;
  4445. }
  4446. VOID
  4447. OutLogCompletionRoutine(
  4448. IN PCOMMAND_PACKET Cmd,
  4449. IN PVOID Arg
  4450. )
  4451. /*++
  4452. Routine Description:
  4453. If a completion event exists in the command packet then
  4454. simply set the event and return. Otherwise, free the command
  4455. packet.
  4456. Arguments:
  4457. Cmd
  4458. Arg - Cmd->CompletionArg
  4459. Return Value:
  4460. None.
  4461. --*/
  4462. {
  4463. #undef DEBSUB
  4464. #define DEBSUB "OutLogCompletionRoutine:"
  4465. DPRINT1(5, "----- OutLog completion 0x%x\n", Cmd);
  4466. if (HANDLE_IS_VALID(Cmd->Parameters.OutLogRequest.CompletionEvent)) {
  4467. SetEvent(Cmd->Parameters.OutLogRequest.CompletionEvent);
  4468. return;
  4469. }
  4470. //
  4471. // Send the packet on to the generic completion routine for freeing
  4472. //
  4473. FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL);
  4474. FrsCompleteCommand(Cmd, Cmd->ErrorStatus);
  4475. }
  4476. ULONG
  4477. OutLogSubmit(
  4478. IN PREPLICA Replica,
  4479. IN PCXTION Cxtion,
  4480. IN USHORT Command
  4481. )
  4482. /*++
  4483. Routine Description:
  4484. Submit a command to the outbound log processor
  4485. Arguments:
  4486. Replica
  4487. Cxtion
  4488. Command
  4489. Return Value:
  4490. None.
  4491. --*/
  4492. {
  4493. #undef DEBSUB
  4494. #define DEBSUB "OutLogSubmit:"
  4495. DWORD WStatus;
  4496. ULONG FStatus;
  4497. PCOMMAND_PACKET Cmd;
  4498. //
  4499. // Allocate a command packet
  4500. //
  4501. Cmd = FrsAllocCommand(&OutLogWork, Command);
  4502. FrsSetCompletionRoutine(Cmd, OutLogCompletionRoutine, NULL);
  4503. Cmd->Parameters.OutLogRequest.Replica = Replica;
  4504. Cmd->Parameters.OutLogRequest.PartnerCxtion = Cxtion;
  4505. Cmd->Parameters.OutLogRequest.CompletionEvent = FrsCreateEvent(TRUE, FALSE);
  4506. DPRINT2(5, "----- Submitting Command 0x%x for %ws\\%ws\\%ws -> %ws\\%ws\n",
  4507. Command, PRINT_CXTION_PATH(Replica, Cxtion));
  4508. //
  4509. // Hand off to the outbound log processor
  4510. //
  4511. WStatus = FrsRtlInsertTailQueue(&OutLogWork, &Cmd->ListEntry);
  4512. if (!WIN_SUCCESS(WStatus)) {
  4513. FRS_CLOSE(Cmd->Parameters.OutLogRequest.CompletionEvent);
  4514. Cmd->Parameters.OutLogRequest.CompletionEvent = NULL;
  4515. FrsCompleteCommand(Cmd, FrsErrorQueueIsRundown);
  4516. return FrsErrorQueueIsRundown;
  4517. }
  4518. //
  4519. // Wait for the command to finish
  4520. //
  4521. WaitForSingleObject(Cmd->Parameters.OutLogRequest.CompletionEvent, INFINITE);
  4522. FStatus = Cmd->ErrorStatus;
  4523. FRS_CLOSE(Cmd->Parameters.OutLogRequest.CompletionEvent);
  4524. Cmd->Parameters.OutLogRequest.CompletionEvent = NULL;
  4525. FrsCompleteCommand(Cmd, Cmd->ErrorStatus);
  4526. DPRINT1_FS(0, "ERROR Submitting %ws\\%ws\\%ws -> %ws\\%ws",
  4527. PRINT_CXTION_PATH(Replica, Cxtion), FStatus);
  4528. return FStatus;
  4529. }
  4530. VOID
  4531. OutLogCopyCxtionToCxtionRecord(
  4532. IN PCXTION Cxtion,
  4533. IN PTABLE_CTX TableCtx
  4534. )
  4535. /*++
  4536. Routine Description:
  4537. Copy the cxtion fields into the cxtion record for DB update.
  4538. Arguments:
  4539. Cxtion
  4540. TableCtx
  4541. Thread Return Value:
  4542. None.
  4543. --*/
  4544. {
  4545. #undef DEBSUB
  4546. #define DEBSUB "OutLogCopyCxtionToCxtionRecord:"
  4547. POUT_LOG_PARTNER OutLogPartner;
  4548. IN PCXTION_RECORD CxtionRecord = TableCtx->pDataRecord;
  4549. //
  4550. // Update the database record in memory
  4551. //
  4552. if (!Cxtion->Name->Name) {
  4553. DPRINT(0, "ERROR - Cxtion's name is NULL!\n");
  4554. Cxtion->Name->Name = FrsWcsDup(L"<unknown>");
  4555. }
  4556. if (!Cxtion->Partner->Name) {
  4557. DPRINT1(0, "ERROR - %ws: Cxtion's partner's name is NULL!\n",
  4558. Cxtion->Name->Name);
  4559. Cxtion->Partner->Name = FrsWcsDup(L"<unknown>");
  4560. }
  4561. if (!Cxtion->PartSrvName) {
  4562. DPRINT1(0, "ERROR - %ws: Cxtion's PartSrvName is NULL!\n",
  4563. Cxtion->Name->Name);
  4564. Cxtion->PartSrvName = FrsWcsDup(L"<unknown>");
  4565. }
  4566. if (!Cxtion->PartnerPrincName) {
  4567. DPRINT1(0, "ERROR - %ws: Cxtion's PartnerPrincName is NULL!\n",
  4568. Cxtion->Name->Name);
  4569. Cxtion->PartnerPrincName = FrsWcsDup(L"<unknown>");
  4570. }
  4571. if (!Cxtion->PartnerSid) {
  4572. DPRINT1(0, "ERROR - %ws: Cxtion's PartnerSid is NULL!\n",
  4573. Cxtion->Name->Name);
  4574. Cxtion->PartnerSid = FrsWcsDup(L"<unknown>");
  4575. }
  4576. //
  4577. // Cxtion Guid and Name
  4578. //
  4579. COPY_GUID(&CxtionRecord->CxtionGuid, Cxtion->Name->Guid);
  4580. wcsncpy(CxtionRecord->CxtionName, Cxtion->Name->Name, MAX_RDN_VALUE_SIZE + 1);
  4581. CxtionRecord->CxtionName[MAX_RDN_VALUE_SIZE] = L'\0';
  4582. //
  4583. // Partner Guid and Name
  4584. //
  4585. COPY_GUID(&CxtionRecord->PartnerGuid, Cxtion->Partner->Guid);
  4586. wcsncpy(CxtionRecord->PartnerName, Cxtion->Partner->Name, MAX_RDN_VALUE_SIZE + 1);
  4587. CxtionRecord->PartnerName[MAX_RDN_VALUE_SIZE] = L'\0';
  4588. //
  4589. // Partner DNS Name
  4590. //
  4591. wcsncpy(CxtionRecord->PartnerDnsName, Cxtion->PartnerDnsName, DNS_MAX_NAME_LENGTH + 1);
  4592. CxtionRecord->PartnerDnsName[DNS_MAX_NAME_LENGTH] = L'\0';
  4593. //
  4594. // Partner PrincName and Server Name
  4595. //
  4596. DbsPackStrW(Cxtion->PartnerPrincName, CrPartnerPrincNamex, TableCtx);
  4597. wcsncpy(CxtionRecord->PartSrvName, Cxtion->PartSrvName, MAX_RDN_VALUE_SIZE + 1);
  4598. CxtionRecord->PartSrvName[MAX_RDN_VALUE_SIZE] = L'\0';
  4599. //
  4600. // Partner SID
  4601. //
  4602. DbsPackStrW(Cxtion->PartnerSid, CrPartnerSidx, TableCtx);
  4603. //
  4604. // Partner Auth Level
  4605. //
  4606. CxtionRecord->PartnerAuthLevel = Cxtion->PartnerAuthLevel;
  4607. //
  4608. // Inbound Flag
  4609. //
  4610. CxtionRecord->Inbound = Cxtion->Inbound;
  4611. //
  4612. // LastJoinTime
  4613. //
  4614. COPY_TIME(&CxtionRecord->LastJoinTime, &Cxtion->LastJoinTime);
  4615. CxtionRecord->TerminationCoSeqNum = Cxtion->TerminationCoSeqNum;
  4616. //
  4617. // Cxtion options.
  4618. //
  4619. CxtionRecord->Options = Cxtion->Options;
  4620. //
  4621. // Cxtion Flags
  4622. // High short belongs to cxtion
  4623. //
  4624. CxtionRecord->Flags &= ~CXTION_FLAGS_CXTION_RECORD_MASK;
  4625. CxtionRecord->Flags |= (Cxtion->Flags & CXTION_FLAGS_CXTION_RECORD_MASK);
  4626. //
  4627. // OUT LOG PARTNER. An inbound connection won't have an OutLogPartner struct.
  4628. //
  4629. OutLogPartner = Cxtion->OLCtx;
  4630. if (OutLogPartner) {
  4631. //
  4632. // Low short belongs to outlogpartner
  4633. //
  4634. CxtionRecord->Flags &= ~OLP_FLAGS_CXTION_RECORD_MASK;
  4635. CxtionRecord->Flags |= (OutLogPartner->Flags & OLP_FLAGS_CXTION_RECORD_MASK);
  4636. CxtionRecord->COLx = OutLogPartner->COLx;
  4637. CxtionRecord->COTx = OutLogPartner->COTx;
  4638. CxtionRecord->COTxNormalModeSave = OutLogPartner->COTxNormalModeSave;
  4639. CxtionRecord->COTslot = OutLogPartner->COTslot;
  4640. CxtionRecord->OutstandingQuota = OutLogPartner->OutstandingQuota;
  4641. CopyMemory(CxtionRecord->AckVector, OutLogPartner->AckVector, ACK_VECTOR_BYTES);
  4642. CxtionRecord->AckVersion = OutLogPartner->AckVersion;
  4643. }
  4644. //
  4645. // Pack the schedule blob
  4646. //
  4647. DbsPackSchedule(Cxtion->Schedule, CrSchedulex, TableCtx);
  4648. }