-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathupdateManager.ts
More file actions
119 lines (98 loc) · 2.67 KB
/
updateManager.ts
File metadata and controls
119 lines (98 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import type { Knex } from 'knex';
export class UpdateManager {
protected db: Knex;
protected tableName = 'directus_settings';
protected rowId = 1;
protected _locking = false;
protected _locked:
| {
hash: string;
ts: string;
}
| false = false;
constructor(database: Knex) {
this.db = database;
}
/**
* Acquire the lock to make updates
* @param newHash - New hash value of latest changes
* @param isoTS - ISO timestamp
* @returns
*/
public async lockForUpdates(newHash: string, isoTS: string) {
if (this._locked || this._locking) return false;
this._locking = true;
// Don't lock if schema sync is not installed yet
const isInstalled = await this.db.schema.hasColumn(this.tableName, 'mv_hash');
if (!isInstalled) {
this._locking = false;
return true;
}
const succeeded = await this.db.transaction(async trx => {
const rows = await trx(this.tableName)
.select('*')
.where('id', this.rowId)
.where('mv_locked', false)
// Only need to migrate if hash is different
.andWhereNot('mv_hash', newHash)
// And only if the previous hash is older than the current one
.andWhere('mv_ts', '<', isoTS)
.orWhereNull('mv_ts')
.forUpdate(); // This locks the row
// If row is found, lock it
if (rows.length) {
await trx(this.tableName).where('id', this.rowId).update({
mv_locked: true,
});
this._locked = {
hash: newHash,
ts: isoTS,
};
return true;
}
return false;
});
this._locking = false;
return succeeded;
}
public async commitUpdates() {
if (!this._locked) return false;
await this.db(this.tableName).where('id', this.rowId).update({
mv_hash: this._locked.hash,
mv_ts: this._locked.ts,
mv_locked: false,
});
this._locked = false;
return true;
}
public async forceCommitUpdates(newHash: string, isoTS: string) {
await this.db(this.tableName).where('id', this.rowId).update({
mv_hash: newHash,
mv_ts: isoTS,
mv_locked: false,
});
this._locked = false;
return true;
}
public async releaseLock() {
if (!this._locked) return false;
await this.db(this.tableName).where('id', this.rowId).update({
mv_locked: false,
});
this._locked = false;
return true;
}
public async ensureInstalled() {
const tableName = 'directus_settings';
const isInstalled = await this.db.schema.hasColumn(tableName, 'mv_hash');
if (!isInstalled) {
await this.db.schema.table(tableName, table => {
table.string('mv_hash').defaultTo('').notNullable();
table.timestamp('mv_ts', { useTz: true }).defaultTo('2020-01-01').notNullable();
table.boolean('mv_locked').defaultTo(false).notNullable();
});
return true;
}
return false;
}
}