Sharding your collection …
{nbsp}this should not take too long.
+
Once your collection is sharded, this tab will show instructions on
@@ -39,4 +66,11 @@ export function ShardingState() {
);
}
-export default connect()(ShardingState);
+export default connect(
+ (state: RootState) => ({
+ isCancellingSharding: state.status === ShardingStatuses.CANCELLING_SHARDING,
+ }),
+ {
+ onCancelSharding: cancelSharding,
+ }
+)(ShardingState);
diff --git a/packages/compass-global-writes/src/plugin-title.spec.tsx b/packages/compass-global-writes/src/plugin-title.spec.tsx
new file mode 100644
index 00000000000..ce4e843a822
--- /dev/null
+++ b/packages/compass-global-writes/src/plugin-title.spec.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { expect } from 'chai';
+import { PluginTitle } from './plugin-title';
+import { render, screen } from '@mongodb-js/testing-library-compass';
+
+describe('PluginTitle', function () {
+ it('Renders a warning', function () {
+ render(
);
+ expect(screen.getByLabelText('warning')).to.be.visible;
+ });
+
+ it('Does not render a warning', function () {
+ render(
);
+ expect(screen.queryByLabelText('warning')).not.to.exist;
+ });
+});
diff --git a/packages/compass-global-writes/src/plugin-title.tsx b/packages/compass-global-writes/src/plugin-title.tsx
index 0c484aecaac..9106ae55c29 100644
--- a/packages/compass-global-writes/src/plugin-title.tsx
+++ b/packages/compass-global-writes/src/plugin-title.tsx
@@ -30,7 +30,7 @@ const iconStylesDark = css({
color: palette.yellow.base,
});
-const PluginTitle = ({ showWarning }: { showWarning: boolean }) => {
+export const PluginTitle = ({ showWarning }: { showWarning: boolean }) => {
const darkMode = useDarkMode();
return (
@@ -50,6 +50,7 @@ const PluginTitle = ({ showWarning }: { showWarning: boolean }) => {
>
false,
+ hasShardingError = () => false,
+ hasShardKey = () => false,
+ failsOnShardingRequest = () => false,
+ authenticatedFetchStub,
+}:
+ | {
+ isNamespaceManaged?: () => boolean;
+ hasShardingError?: () => boolean;
+ hasShardKey?: () => boolean;
+ failsOnShardingRequest?: () => boolean;
+ authenticatedFetchStub?: never;
+ }
+ | {
+ isNamespaceManaged?: never;
+ hasShardingError?: never;
+ hasShardKey?: () => boolean;
+ failsOnShardingRequest?: never;
+ authenticatedFetchStub?: () => void;
+ } = {}): GlobalWritesStore {
+ const atlasService = {
+ authenticatedFetch: (uri: string) => {
+ if (uri.includes(`/geoSharding`) && failsOnShardingRequest()) {
+ return Promise.reject(new Error('Failed to shard'));
+ }
+
+ if (uri.includes('/clusters/')) {
+ return createAuthFetchResponse({
+ ...clusterDetails,
+ geoSharding: {
+ ...clusterDetails.geoSharding,
+ managedNamespaces: isNamespaceManaged() ? [managedNamespace] : [],
+ },
+ });
+ }
+
+ if (uri.includes('/deploymentStatus/')) {
+ return createAuthFetchResponse({
+ automationStatus: {
+ processes: hasShardingError() ? [failedShardingProcess] : [],
+ },
+ });
+ }
+
+ return createAuthFetchResponse({});
+ },
+ automationAgentRequest: (_meta: unknown, type: string) => ({
+ _id: '123',
+ requestType: type,
+ }),
+ automationAgentAwait: (_meta: unknown, type: string) => {
+ if (type === 'getShardKey') {
+ return {
+ response: hasShardKey()
+ ? [
+ {
+ key: {
+ location: 'range',
+ secondary: shardKeyData.isCustomShardKeyHashed
+ ? 'hashed'
+ : 'range',
+ },
+ unique: true,
+ },
+ ]
+ : [],
+ };
+ }
+ },
+ } as any;
+
+ if (authenticatedFetchStub)
+ atlasService.authenticatedFetch = authenticatedFetchStub;
+
return setupStore(
{
namespace: NS,
@@ -69,6 +150,20 @@ function createStore(atlasService: any = {}): GlobalWritesStore {
}
describe('GlobalWritesStore Store', function () {
+ let confirmationStub: Sinon.SinonStub;
+ let clock: Sinon.SinonFakeTimers;
+
+ beforeEach(() => {
+ confirmationStub = sinon
+ .stub(globalWritesReducer, 'showConfirmation')
+ .resolves(true);
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ clock && clock.restore();
+ });
+
it('sets the initial state', function () {
const store = createStore();
expect(store.getState().namespace).to.equal(NS);
@@ -76,88 +171,184 @@ describe('GlobalWritesStore Store', function () {
});
context('scenarios', function () {
- it('not managed -> sharding', async function () {
+ it('not managed -> sharding -> valid shard key', async function () {
+ let mockShardKey = false;
+ // initial state === unsharded
const store = createStore({
- authenticatedFetch: () => createAuthFetchResponse(clusterDetails),
+ hasShardKey: Sinon.fake(() => mockShardKey),
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('UNSHARDED');
+ expect(store.getState().managedNamespace).to.equal(undefined);
});
- await store.dispatch(fetchClusterShardingData());
- expect(store.getState().status).to.equal('UNSHARDED');
- expect(store.getState().managedNamespace).to.equal(undefined);
+ // user requests sharding
+ clock = sinon.useFakeTimers({
+ shouldAdvanceTime: true,
+ });
const promise = store.dispatch(createShardKey(shardKeyData));
expect(store.getState().status).to.equal('SUBMITTING_FOR_SHARDING');
await promise;
expect(store.getState().status).to.equal('SHARDING');
+
+ // sharding ends with a shardKey
+ mockShardKey = true;
+ clock.tick(POLLING_INTERVAL);
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
+ });
});
- it('not managed -> failed sharding attempt', async function () {
+ it('not managed -> sharding -> sharding error', async function () {
+ let mockFailure = false;
+ // initial state === unsharded
const store = createStore({
- authenticatedFetch: (uri: string) => {
- if (uri.includes('/geoSharding')) {
- return Promise.reject(new Error('Failed to shard'));
- }
+ hasShardingError: Sinon.fake(() => mockFailure),
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('UNSHARDED');
+ expect(store.getState().managedNamespace).to.equal(undefined);
+ });
- return createAuthFetchResponse(clusterDetails);
- },
+ // user requests sharding
+ clock = sinon.useFakeTimers({
+ shouldAdvanceTime: true,
});
- await store.dispatch(fetchClusterShardingData());
- expect(store.getState().status).to.equal('UNSHARDED');
- expect(store.getState().managedNamespace).to.equal(undefined);
+ const promise = store.dispatch(createShardKey(shardKeyData));
+ expect(store.getState().status).to.equal('SUBMITTING_FOR_SHARDING');
+ await promise;
+ expect(store.getState().status).to.equal('SHARDING');
+
+ // sharding ends with an error
+ mockFailure = true;
+ clock.tick(POLLING_INTERVAL);
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARDING_ERROR');
+ });
+ });
+ it('not managed -> not managed (failed sharding request)', async function () {
+ // initial state === not managed
+ const store = createStore({
+ failsOnShardingRequest: () => true,
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('UNSHARDED');
+ expect(store.getState().managedNamespace).to.equal(undefined);
+ });
+
+ // user tries to submit for sharding, but the request fails
const promise = store.dispatch(createShardKey(shardKeyData));
expect(store.getState().status).to.equal('SUBMITTING_FOR_SHARDING');
await promise;
expect(store.getState().status).to.equal('UNSHARDED');
});
- it('when the namespace is managed', async function () {
+ it('sharding -> valid shard key', async function () {
+ let mockShardKey = false;
+ // initial state === sharding
+ clock = sinon.useFakeTimers({
+ shouldAdvanceTime: true,
+ });
+ const store = createStore({
+ isNamespaceManaged: () => true,
+ hasShardKey: Sinon.fake(() => mockShardKey),
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARDING');
+ expect(store.getState().managedNamespace).to.equal(managedNamespace);
+ });
+
+ // sharding ends with a shardKey
+ mockShardKey = true;
+ clock.tick(POLLING_INTERVAL);
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
+ });
+ });
+
+ it('sharding -> cancelling request -> not managed', async function () {
+ let mockManagedNamespace = true;
+ confirmationStub.resolves(true);
+ // initial state === sharding
+ const store = createStore({
+ isNamespaceManaged: Sinon.fake(() => mockManagedNamespace),
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARDING');
+ expect(store.getState().pollingTimeout).not.to.be.undefined;
+ expect(store.getState().managedNamespace).to.equal(managedNamespace);
+ });
+
+ // user cancels the sharding request
+ const promise = store.dispatch(cancelSharding());
+ mockManagedNamespace = false;
+ await promise;
+ expect(store.getState().status).to.equal('UNSHARDED');
+ expect(store.getState().pollingTimeout).to.be.undefined;
+ expect(confirmationStub).to.have.been.called;
+ });
+
+ it('valid shard key', async function () {
const store = createStore({
- authenticatedFetch: (uri: string) => {
- if (uri.includes('/clusters/')) {
- return createAuthFetchResponse({
- ...clusterDetails,
- geoSharding: {
- ...clusterDetails.geoSharding,
- managedNamespaces: [managedNamespace],
- },
- });
- }
-
- if (uri.includes('/deploymentStatus/')) {
- return createAuthFetchResponse({
- automationStatus: {
- processes: [],
- },
- });
- }
-
- return createAuthFetchResponse({});
- },
- automationAgentRequest: (_meta: unknown, type: string) => ({
- _id: '123',
- requestType: type,
- }),
- automationAgentAwait: (_meta: unknown, type: string) => {
- if (type === 'getShardKey') {
- return {
- response: [
- {
- key: {
- location: 'HASHED',
- secondary: 'HASHED',
- },
- unique: false,
- },
- ],
- };
- }
- },
+ isNamespaceManaged: () => true,
+ hasShardKey: () => true,
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
+ expect(store.getState().managedNamespace).to.equal(managedNamespace);
+ });
+ });
+
+ it('valid shard key -> not managed', async function () {
+ // initial state === shard key correct
+ const store = createStore({
+ isNamespaceManaged: () => true,
+ hasShardKey: () => true,
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
+ expect(store.getState().managedNamespace).to.equal(managedNamespace);
+ });
+
+ // user asks to unmanage
+ const promise = store.dispatch(unmanageNamespace());
+ expect(store.getState().status).to.equal('UNMANAGING_NAMESPACE');
+ await promise;
+ expect(store.getState().status).to.equal('UNSHARDED');
+ });
+
+ it('valid shard key -> valid shard key (failed unmanage attempt)', async function () {
+ // initial state === shard key correct
+ let mockFailure = false;
+ const store = createStore({
+ isNamespaceManaged: () => true,
+ hasShardKey: () => true,
+ failsOnShardingRequest: Sinon.fake(() => mockFailure),
});
- await store.dispatch(fetchClusterShardingData());
+
await waitFor(() => {
expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
expect(store.getState().managedNamespace).to.equal(managedNamespace);
});
+
+ // user asks to unmanage
+ mockFailure = true;
+ const promise = store.dispatch(unmanageNamespace());
+ expect(store.getState().status).to.equal('UNMANAGING_NAMESPACE');
+ await promise;
+ expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
+ });
+
+ it('sharding error', async function () {
+ const store = createStore({
+ isNamespaceManaged: () => true,
+ hasShardingError: () => true,
+ });
+ await waitFor(() => {
+ expect(store.getState().status).to.equal('SHARDING_ERROR');
+ expect(store.getState().managedNamespace).to.equal(managedNamespace);
+ });
});
it('sends correct data to the server when creating a shard key', async function () {
@@ -165,7 +356,7 @@ describe('GlobalWritesStore Store', function () {
{
db: 'test',
collection: 'one',
- customShardKey: 'test',
+ customShardKey: 'secondary',
isCustomShardKeyHashed: true,
isShardKeyUnique: false,
numInitialChunks: 1,
@@ -196,7 +387,7 @@ describe('GlobalWritesStore Store', function () {
.resolves();
const store = createStore({
- authenticatedFetch: fetchStub,
+ authenticatedFetchStub: fetchStub,
});
await store.dispatch(createShardKey(shardKeyData));
diff --git a/packages/compass-global-writes/src/store/reducer.ts b/packages/compass-global-writes/src/store/reducer.ts
index 85981a039df..d596d52d51c 100644
--- a/packages/compass-global-writes/src/store/reducer.ts
+++ b/packages/compass-global-writes/src/store/reducer.ts
@@ -1,8 +1,13 @@
import type { Action, Reducer } from 'redux';
import type { GlobalWritesThunkAction } from '.';
-import { openToast } from '@mongodb-js/compass-components';
+import {
+ openToast,
+ showConfirmation as showConfirmationModal,
+} from '@mongodb-js/compass-components';
import type { ManagedNamespace } from '../services/atlas-global-writes-service';
+export const POLLING_INTERVAL = 5000;
+
export function isAction(
action: Action,
type: A['type']
@@ -30,6 +35,13 @@ enum GlobalWritesActionTypes {
SubmittingForShardingFinished = 'global-writes/SubmittingForShardingFinished',
SubmittingForShardingErrored = 'global-writes/SubmittingForShardingErrored',
+ CancellingShardingStarted = 'global-writes/CancellingShardingStarted',
+ CancellingShardingFinished = 'global-writes/CancellingShardingFinished',
+ CancellingShardingErrored = 'global-writes/CancellingShardingErrored',
+
+ NextPollingTimeoutSet = 'global-writes/NextPollingTimeoutSet',
+ NextPollingTimeoutCleared = 'global-writes/NextPollingTimeoutCleared',
+
UnmanagingNamespaceStarted = 'global-writes/UnmanagingNamespaceStarted',
UnmanagingNamespaceFinished = 'global-writes/UnmanagingNamespaceFinished',
UnmanagingNamespaceErrored = 'global-writes/UnmanagingNamespaceErrored',
@@ -68,6 +80,28 @@ type SubmittingForShardingErroredAction = {
type: GlobalWritesActionTypes.SubmittingForShardingErrored;
};
+type CancellingShardingStartedAction = {
+ type: GlobalWritesActionTypes.CancellingShardingStarted;
+};
+
+type CancellingShardingFinishedAction = {
+ type: GlobalWritesActionTypes.CancellingShardingFinished;
+ managedNamespace?: ManagedNamespace;
+};
+
+type CancellingShardingErroredAction = {
+ type: GlobalWritesActionTypes.CancellingShardingErrored;
+};
+
+type NextPollingTimeoutSetAction = {
+ type: GlobalWritesActionTypes.NextPollingTimeoutSet;
+ timeout: NodeJS.Timeout;
+};
+
+type NextPollingTimeoutClearedAction = {
+ type: GlobalWritesActionTypes.NextPollingTimeoutCleared;
+};
+
type UnmanagingNamespaceStartedAction = {
type: GlobalWritesActionTypes.UnmanagingNamespaceStarted;
};
@@ -102,6 +136,12 @@ export enum ShardingStatuses {
*/
SHARDING = 'SHARDING',
+ /**
+ * State when user cancels the sharding and
+ * we are waiting for server to accept the request.
+ */
+ CANCELLING_SHARDING = 'CANCELLING_SHARDING',
+
/**
* Sharding failed.
*/
@@ -157,23 +197,36 @@ export type RootState = {
status: ShardingStatuses.NOT_READY;
shardKey?: never;
shardingError?: never;
+ pollingTimeout?: never;
}
| {
status:
| ShardingStatuses.UNSHARDED
| ShardingStatuses.SUBMITTING_FOR_SHARDING
- | ShardingStatuses.SHARDING;
+ | ShardingStatuses.CANCELLING_SHARDING;
/**
* note: shardKey might exist even for unsharded.
* if the collection was sharded previously and then unmanaged
*/
shardKey?: ShardKey;
shardingError?: never;
+ pollingTimeout?: never;
+ }
+ | {
+ status: ShardingStatuses.SHARDING;
+ /**
+ * note: shardKey might exist
+ * if the collection was sharded previously and then unmanaged
+ */
+ shardKey?: ShardKey;
+ shardingError?: never;
+ pollingTimeout?: NodeJS.Timeout;
}
| {
status: ShardingStatuses.SHARDING_ERROR;
shardKey?: never;
shardingError: string;
+ pollingTimeout?: never;
}
| {
status:
@@ -183,6 +236,7 @@ export type RootState = {
| ShardingStatuses.UNMANAGING_NAMESPACE;
shardKey: ShardKey;
shardingError?: never;
+ pollingTimeout?: never;
}
);
@@ -214,13 +268,18 @@ const reducer: Reducer = (state = initialState, action) => {
action,
GlobalWritesActionTypes.NamespaceShardingErrorFetched
) &&
- state.status === ShardingStatuses.NOT_READY
+ (state.status === ShardingStatuses.NOT_READY ||
+ state.status === ShardingStatuses.SHARDING)
) {
+ if (state.pollingTimeout) {
+ throw new Error('Polling was not stopped');
+ }
return {
...state,
status: ShardingStatuses.SHARDING_ERROR,
shardKey: undefined,
shardingError: action.error,
+ pollingTimeout: state.pollingTimeout,
};
}
@@ -229,13 +288,18 @@ const reducer: Reducer = (state = initialState, action) => {
action,
GlobalWritesActionTypes.NamespaceShardKeyFetched
) &&
- state.status === ShardingStatuses.NOT_READY
+ (state.status === ShardingStatuses.NOT_READY ||
+ state.status === ShardingStatuses.SHARDING)
) {
+ if (state.pollingTimeout) {
+ throw new Error('Polling was not stopped');
+ }
return {
...state,
status: getStatusFromShardKey(action.shardKey, state.managedNamespace),
shardKey: action.shardKey,
shardingError: undefined,
+ pollingTimeout: state.pollingTimeout,
};
}
@@ -269,7 +333,8 @@ const reducer: Reducer = (state = initialState, action) => {
action,
GlobalWritesActionTypes.SubmittingForShardingFinished
) &&
- state.status === ShardingStatuses.SUBMITTING_FOR_SHARDING
+ (state.status === ShardingStatuses.SUBMITTING_FOR_SHARDING ||
+ state.status === ShardingStatuses.NOT_READY)
) {
return {
...state,
@@ -278,6 +343,78 @@ const reducer: Reducer = (state = initialState, action) => {
};
}
+ if (
+ isAction(
+ action,
+ GlobalWritesActionTypes.NextPollingTimeoutSet
+ ) &&
+ state.status === ShardingStatuses.SHARDING
+ ) {
+ return {
+ ...state,
+ pollingTimeout: action.timeout,
+ };
+ }
+
+ if (
+ isAction(
+ action,
+ GlobalWritesActionTypes.NextPollingTimeoutCleared
+ ) &&
+ state.status === ShardingStatuses.SHARDING
+ ) {
+ return {
+ ...state,
+ pollingTimeout: undefined,
+ };
+ }
+
+ if (
+ isAction(
+ action,
+ GlobalWritesActionTypes.CancellingShardingStarted
+ ) &&
+ state.status === ShardingStatuses.SHARDING
+ ) {
+ if (state.pollingTimeout) {
+ throw new Error('Polling was not stopped');
+ }
+ return {
+ ...state,
+ status: ShardingStatuses.CANCELLING_SHARDING,
+ pollingTimeout: state.pollingTimeout,
+ };
+ }
+
+ if (
+ isAction(
+ action,
+ GlobalWritesActionTypes.CancellingShardingErrored
+ ) &&
+ state.status === ShardingStatuses.CANCELLING_SHARDING
+ ) {
+ return {
+ ...state,
+ status: ShardingStatuses.SHARDING,
+ };
+ }
+
+ if (
+ isAction(
+ action,
+ GlobalWritesActionTypes.CancellingShardingFinished
+ ) &&
+ (state.status === ShardingStatuses.CANCELLING_SHARDING ||
+ state.status === ShardingStatuses.SHARDING_ERROR)
+ // the error might come before the cancel request was processed
+ ) {
+ return {
+ ...state,
+ status: ShardingStatuses.UNSHARDED,
+ shardingError: undefined,
+ };
+ }
+
if (
isAction(
action,
@@ -433,6 +570,64 @@ export const createShardKey = (
};
};
+// Exporting this for test only to stub it and set
+// its value. This enables to test cancelSharding action.
+export const showConfirmation = showConfirmationModal;
+
+export const cancelSharding = (): GlobalWritesThunkAction<
+ Promise,
+ | CancellingShardingStartedAction
+ | CancellingShardingFinishedAction
+ | CancellingShardingErroredAction
+> => {
+ return async (dispatch, getState, { atlasGlobalWritesService, logger }) => {
+ const confirmed = await showConfirmation({
+ title: 'Confirmation',
+ description: 'Are you sure you want to cancel the sharding request?',
+ });
+
+ if (!confirmed) {
+ return;
+ }
+
+ const { namespace, status } = getState();
+
+ if (status === ShardingStatuses.SHARDING) {
+ dispatch(stopPollingForShardKey());
+ }
+ dispatch({
+ type: GlobalWritesActionTypes.CancellingShardingStarted,
+ });
+
+ try {
+ await atlasGlobalWritesService.unmanageNamespace(namespace);
+ dispatch({
+ type: GlobalWritesActionTypes.CancellingShardingFinished,
+ });
+ } catch (error) {
+ logger.log.error(
+ logger.mongoLogId(1_001_000_334),
+ 'AtlasFetchError',
+ 'Error cancelling the sharding process',
+ {
+ error: (error as Error).message,
+ }
+ );
+ openToast('global-writes-cancel-sharding-error', {
+ title: `Failed to cancel the sharding process: ${
+ (error as Error).message
+ }`,
+ dismissible: true,
+ timeout: 5000,
+ variant: 'important',
+ });
+ dispatch({
+ type: GlobalWritesActionTypes.CancellingShardingErrored,
+ });
+ }
+ };
+};
+
const setNamespaceBeingSharded = (
managedNamespace?: ManagedNamespace
): GlobalWritesThunkAction => {
@@ -441,6 +636,41 @@ const setNamespaceBeingSharded = (
type: GlobalWritesActionTypes.SubmittingForShardingFinished,
managedNamespace,
});
+ dispatch(pollForShardKey());
+ };
+};
+
+const pollForShardKey = (): GlobalWritesThunkAction<
+ void,
+ NextPollingTimeoutSetAction
+> => {
+ return (dispatch, getState) => {
+ const { pollingTimeout } = getState();
+ if (
+ pollingTimeout // prevent double polling
+ ) {
+ return;
+ }
+ const timeout = setTimeout(() => {
+ void dispatch(fetchNamespaceShardKey());
+ }, POLLING_INTERVAL);
+
+ dispatch({
+ type: GlobalWritesActionTypes.NextPollingTimeoutSet,
+ timeout,
+ });
+ };
+};
+
+const stopPollingForShardKey = (): GlobalWritesThunkAction<
+ void,
+ NextPollingTimeoutClearedAction
+> => {
+ return (dispatch, getState) => {
+ const { pollingTimeout } = getState();
+ if (!pollingTimeout) return;
+ clearTimeout(pollingTimeout);
+ dispatch({ type: GlobalWritesActionTypes.NextPollingTimeoutCleared });
};
};
@@ -453,7 +683,7 @@ export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
getState,
{ atlasGlobalWritesService, logger, connectionInfoRef }
) => {
- const { namespace } = getState();
+ const { namespace, status } = getState();
try {
const [shardingError, shardKey] = await Promise.all([
@@ -462,6 +692,9 @@ export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
]);
if (shardingError) {
+ if (status === ShardingStatuses.SHARDING) {
+ dispatch(stopPollingForShardKey());
+ }
dispatch({
type: GlobalWritesActionTypes.NamespaceShardingErrorFetched,
error: shardingError,
@@ -474,6 +707,9 @@ export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
return;
}
+ if (status === ShardingStatuses.SHARDING) {
+ dispatch(stopPollingForShardKey());
+ }
dispatch({
type: GlobalWritesActionTypes.NamespaceShardKeyFetched,
shardKey,