Skip to content

Commit ade2b12

Browse files
committed
[DE-583] Implement cluster rebalancing
1 parent 3c3d837 commit ade2b12

File tree

2 files changed

+274
-0
lines changed

2 files changed

+274
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ This driver uses semantic versioning:
3131

3232
- Added missing attributes to `QueryInfo` and `MultiExplainResult.stats` types (DE-607)
3333

34+
- Added cluster rebalancing methods to `Database` (DE-583)
35+
3436
## [8.3.1] - 2023-06-05
3537

3638
### Changed

src/database.ts

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,176 @@ export type QueryInfo = {
661661
stream: boolean;
662662
};
663663

664+
/**
665+
* Information about a cluster imbalance.
666+
*/
667+
export type ClusterImbalanceInfo = {
668+
/**
669+
* Information about the leader imbalance.
670+
*/
671+
leader: {
672+
/**
673+
* The weight of leader shards per DB-Server. A leader has a weight of 1 by default but it is higher if collections can only be moved together because of `distributeShardsLike`.
674+
*/
675+
weightUsed: number[];
676+
/**
677+
* The ideal weight of leader shards per DB-Server.
678+
*/
679+
targetWeight: number[];
680+
/**
681+
* The number of leader shards per DB-Server.
682+
*/
683+
numberShards: number[];
684+
/**
685+
* The measure of the leader shard distribution. The higher the number, the worse the distribution.
686+
*/
687+
leaderDupl: number[];
688+
/**
689+
* The sum of all weights.
690+
*/
691+
totalWeight: number;
692+
/**
693+
* The measure of the total imbalance. A high value indicates a high imbalance.
694+
*/
695+
imbalance: number;
696+
/**
697+
* The sum of shards, counting leader shards only.
698+
*/
699+
totalShards: number;
700+
};
701+
/**
702+
* Information about the shard imbalance.
703+
*/
704+
shards: {
705+
/**
706+
* The size of shards per DB-Server.
707+
*/
708+
sizeUsed: number[];
709+
/**
710+
* The ideal size of shards per DB-Server.
711+
*/
712+
targetSize: number[];
713+
/**
714+
* The number of leader and follower shards per DB-Server.
715+
*/
716+
numberShards: number[];
717+
/**
718+
* The sum of the sizes.
719+
*/
720+
totalUsed: number;
721+
/**
722+
* The sum of shards, counting leader and follower shards.
723+
*/
724+
totalShards: number;
725+
/**
726+
* The sum of system collection shards, counting leader shards only.
727+
*/
728+
totalShardsFromSystemCollections: number;
729+
/**
730+
* The measure of the total imbalance. A high value indicates a high imbalance.
731+
*/
732+
imbalance: number;
733+
};
734+
};
735+
736+
/**
737+
* Information about the current state of the cluster imbalance.
738+
*/
739+
export type ClusterRebalanceState = ClusterImbalanceInfo & {
740+
/**
741+
* The number of pending move shard operations.
742+
*/
743+
pendingMoveShards: number;
744+
/**
745+
* The number of planned move shard operations.
746+
*/
747+
todoMoveShards: number;
748+
};
749+
750+
/**
751+
* Options for rebalancing the cluster.
752+
*/
753+
export type ClusterRebalanceOptions = {
754+
/**
755+
* Maximum number of moves to be computed.
756+
*
757+
* Default: `1000`
758+
*/
759+
maximumNumberOfMoves?: number;
760+
/**
761+
* Allow leader changes without moving data.
762+
*
763+
* Default: `true`
764+
*/
765+
leaderChanges?: boolean;
766+
/**
767+
* Allow moving leaders.
768+
*
769+
* Default: `false`
770+
*/
771+
moveLeaders?: boolean;
772+
/**
773+
* Allow moving followers.
774+
*
775+
* Default: `false`
776+
*/
777+
moveFollowers?: boolean;
778+
/**
779+
* Ignore system collections in the rebalance plan.
780+
*
781+
* Default: `false`
782+
*/
783+
excludeSystemCollections?: boolean;
784+
/**
785+
* Default: `256**6`
786+
*/
787+
piFactor?: number;
788+
/**
789+
* A list of database names to exclude from the analysis.
790+
*
791+
* Default: `[]`
792+
*/
793+
databasesExcluded?: string[];
794+
};
795+
796+
export type ClusterRebalanceMove = {
797+
/**
798+
* The server name from which to move.
799+
*/
800+
from: string;
801+
/**
802+
* The ID of the destination server.
803+
*/
804+
to: string;
805+
/**
806+
* Shard ID of the shard to be moved.
807+
*/
808+
shard: string;
809+
/**
810+
* Collection ID of the collection the shard belongs to.
811+
*/
812+
collection: number;
813+
/**
814+
* True if this is a leader move shard operation.
815+
*/
816+
isLeader: boolean;
817+
};
818+
819+
export type ClusterRebalanceResult = {
820+
/**
821+
* Imbalance before the suggested move shard operations are applied.
822+
*/
823+
imbalanceBefore: ClusterImbalanceInfo;
824+
/**
825+
* Expected imbalance after the suggested move shard operations are applied.
826+
*/
827+
imbalanceAfter: ClusterImbalanceInfo;
828+
/**
829+
* Suggested move shard operations.
830+
*/
831+
moves: ClusterRebalanceMove[];
832+
};
833+
664834
/**
665835
* Database user to create with a database.
666836
*/
@@ -1843,6 +2013,108 @@ export class Database {
18432013
}
18442014
//#endregion
18452015

2016+
//#region rebalancing
2017+
/**
2018+
* Computes the current cluster imbalance.
2019+
*
2020+
* @example
2021+
* ```js
2022+
* const db = new Database();
2023+
* const imbalance = await db.getClusterImbalance();
2024+
* ```
2025+
*/
2026+
getClusterImbalance(): Promise<ClusterRebalanceState> {
2027+
return this.request(
2028+
{ path: "/_admin/cluster/rebalance" },
2029+
(res) => res.body.result
2030+
);
2031+
}
2032+
2033+
/**
2034+
* Computes a set of move shard operations to rebalance the cluster.
2035+
*
2036+
* @example
2037+
* ```js
2038+
* const db = new Database();
2039+
* const result = await db.computerClusterRebalance({
2040+
* moveLeaders: true,
2041+
* moveFollowers: true
2042+
* });
2043+
* if (result.moves.length) {
2044+
* await db.executeClusterRebalance(result.moves);
2045+
* }
2046+
* ```
2047+
*/
2048+
computeClusterRebalance(
2049+
opts: ClusterRebalanceOptions
2050+
): Promise<ClusterRebalanceResult> {
2051+
return this.request(
2052+
{
2053+
method: "POST",
2054+
path: "/_admin/cluster/rebalance",
2055+
body: {
2056+
version: 1,
2057+
...opts,
2058+
},
2059+
},
2060+
(res) => res.body.result
2061+
);
2062+
}
2063+
2064+
/**
2065+
* Executes the given cluster move shard operations.
2066+
*
2067+
* @example
2068+
* ```js
2069+
* const db = new Database();
2070+
* const result = await db.computerClusterRebalance({
2071+
* moveLeaders: true,
2072+
* moveFollowers: true
2073+
* });
2074+
* if (result.moves.length) {
2075+
* await db.executeClusterRebalance(result.moves);
2076+
* }
2077+
* ```
2078+
*/
2079+
executeClusterRebalance(moves: ClusterRebalanceMove[]): Promise<unknown> {
2080+
return this.request({
2081+
method: "POST",
2082+
path: "/_admin/cluster/rebalance/execute",
2083+
body: {
2084+
version: 1,
2085+
moves,
2086+
},
2087+
});
2088+
}
2089+
2090+
/**
2091+
* Computes a set of move shard operations to rebalance the cluster and
2092+
* executes them.
2093+
*
2094+
* @example
2095+
* ```js
2096+
* const db = new Database();
2097+
* const result = await db.rebalanceCluster({
2098+
* moveLeaders: true,
2099+
* moveFollowers: true
2100+
* });
2101+
* // The cluster is now rebalanced.
2102+
* ```
2103+
*/
2104+
rebalanceCluster(
2105+
opts: ClusterRebalanceOptions
2106+
): Promise<ClusterRebalanceResult> {
2107+
return this.request({
2108+
method: "PUT",
2109+
path: "/_admin/cluster/rebalance",
2110+
body: {
2111+
version: 1,
2112+
...opts,
2113+
},
2114+
});
2115+
}
2116+
//#endregion
2117+
18462118
//#region databases
18472119
/**
18482120
* Creates a new `Database` instance for the given `databaseName` that

0 commit comments

Comments
 (0)