Source code of Windows XP (NT5)
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.

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