Skip to content

Commit 818c857

Browse files
authored
feat: Adds migration locking. (#3)
BREAKING CHANGE: Requires `lockMigrations` and `unlockMigrations` in `RepoFacade`.
1 parent 445efc3 commit 818c857

File tree

7 files changed

+59
-21
lines changed

7 files changed

+59
-21
lines changed

src/RepoFacade.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export default interface RepoFacade {
55
readonly updateProcessedMigration: (migration: ProcessedMigration) => Promise<void>;
66
readonly removeProcessedMigration: (key: string) => Promise<void>;
77
readonly clearMigrations: () => Promise<void>;
8+
readonly lockMigrations: () => Promise<void>;
9+
readonly unlockMigrations: () => Promise<void>;
810
}

src/factory.test.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ sourceMapSupport.install();
44
import factoryTest from './factoryTest';
55
import ProcessedMigration from './utils/types/ProcessedMigration';
66

7-
// tslint:disable-next-line:no-let
8-
let processedMigrations: ProcessedMigration[] = [];
7+
let processedMigrations: ProcessedMigration[] = []; // tslint:disable-line:no-let
8+
let hasLockedMigrations = false; // tslint:disable-line:no-let
99

1010
factoryTest({
1111
clearMigrations: async () => {
@@ -14,11 +14,20 @@ factoryTest({
1414
getProcessedMigrations: async () => {
1515
return processedMigrations;
1616
},
17+
lockMigrations: async () => {
18+
if (hasLockedMigrations) {
19+
throw new Error();
20+
}
21+
hasLockedMigrations = true;
22+
},
1723
removeProcessedMigration: async (key) => {
1824
processedMigrations = processedMigrations.filter((processedMigration) => {
1925
return processedMigration.key !== key;
2026
});
2127
},
28+
unlockMigrations: async () => {
29+
hasLockedMigrations = false;
30+
},
2231
updateProcessedMigration: async (migration) => {
2332
const unmatchedMigrations = processedMigrations.filter((processedMigration) => {
2433
return processedMigration.key !== migration.key;

src/migrate/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { reduce } from 'bluebird';
22
import FacadeConfig from '../FacadeConfig';
33
import getUnprocessedKeys from '../utils/getUnprocessedKeys';
4+
import handleLocks from '../utils/handleLocks';
45
import migrateKey from '../utils/migrateKey';
56
import Signature from './Signature';
67

78
export default (config: FacadeConfig): Signature => {
89
return async () => {
9-
const unprocessedKeys = await getUnprocessedKeys(config);
10-
const batchStart = new Date();
10+
await handleLocks(config, async () => {
11+
const unprocessedKeys = await getUnprocessedKeys(config);
12+
const batchStart = new Date();
1113

12-
await Promise.resolve(reduce(unprocessedKeys, async (_result, key) => {
13-
await migrateKey({ config, key, batchStart });
14-
}, Promise.resolve()));
14+
await Promise.resolve(reduce(unprocessedKeys, async (_result, key) => {
15+
await migrateKey({ config, key, batchStart });
16+
}, Promise.resolve()));
17+
});
1518
};
1619
};

src/migrateByKey/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import FacadeConfig from '../FacadeConfig';
22
import ProcessedMigrationError from '../utils/errors/ProcessedMigrationError';
3+
import handleLocks from '../utils/handleLocks';
34
import hasProcessedKey from '../utils/hasProcessedKey';
45
import migrateKey from '../utils/migrateKey';
56
import Signature from './Signature';
67

78
export default (config: FacadeConfig): Signature => {
89
return async ({ key, force = false }) => {
9-
const isProcessed = await hasProcessedKey(config, key);
10-
if (isProcessed && !force) {
11-
throw new ProcessedMigrationError(key);
12-
}
10+
await handleLocks(config, async () => {
11+
const isProcessed = await hasProcessedKey(config, key);
12+
if (isProcessed && !force) {
13+
throw new ProcessedMigrationError(key);
14+
}
1315

14-
await migrateKey({ config, key });
16+
await migrateKey({ config, key });
17+
});
1518
};
1619
};

src/rollback/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import { reduce } from 'bluebird';
22
import FacadeConfig from '../FacadeConfig';
33
import getLastBatchKeys from '../utils/getLastBatchKeys';
4+
import handleLocks from '../utils/handleLocks';
45
import rollbackKey from '../utils/rollbackKey';
56
import Signature from './Signature';
67

78
export default (config: FacadeConfig): Signature => {
89
return async () => {
9-
const lastBatchKeys = await getLastBatchKeys(config);
10+
await handleLocks(config, async () => {
11+
const lastBatchKeys = await getLastBatchKeys(config);
1012

11-
await Promise.resolve(reduce(lastBatchKeys, async (_result, key) => {
12-
await rollbackKey({ config, key });
13-
}, Promise.resolve()));
13+
await Promise.resolve(reduce(lastBatchKeys, async (_result, key) => {
14+
await rollbackKey({ config, key });
15+
}, Promise.resolve()));
16+
});
1417
};
1518
};

src/rollbackByKey/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import FacadeConfig from '../FacadeConfig';
22
import UnprocessedMigrationError from '../utils/errors/UnprocessedMigrationError';
3+
import handleLocks from '../utils/handleLocks';
34
import hasProcessedKey from '../utils/hasProcessedKey';
45
import rollbackKey from '../utils/rollbackKey';
56
import Signature from './Signature';
67

78
export default (config: FacadeConfig): Signature => {
89
return async ({ key, force = false }) => {
9-
const isProcessed = await hasProcessedKey(config, key);
10-
if (!isProcessed && !force) {
11-
throw new UnprocessedMigrationError(key);
12-
}
10+
await handleLocks(config, async () => {
11+
const isProcessed = await hasProcessedKey(config, key);
12+
if (!isProcessed && !force) {
13+
throw new UnprocessedMigrationError(key);
14+
}
1315

14-
await rollbackKey({ config, key });
16+
await rollbackKey({ config, key });
17+
});
1518
};
1619
};

src/utils/handleLocks.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import FacadeConfig from '../FacadeConfig';
2+
3+
export default async (config: FacadeConfig, handler: () => Promise<void>) => {
4+
await config.repo.lockMigrations();
5+
config.log('Locked migrations');
6+
try {
7+
await handler();
8+
config.log('Unlocked migrations after completion');
9+
await config.repo.unlockMigrations();
10+
} catch (err) {
11+
config.log('Unlocked migrations after error');
12+
await config.repo.unlockMigrations();
13+
throw err;
14+
}
15+
};

0 commit comments

Comments
 (0)