@kapeta/local-cluster-service 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.11.1](https://github.com/kapetacom/local-cluster-service/compare/v0.11.0...v0.11.1) (2023-07-31)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Ensure we do not attempt to start / stop the same instance at the ([493e077](https://github.com/kapetacom/local-cluster-service/commit/493e077d0c6acdbcc371dae2ef8b6fbf2478c950))
7
+
1
8
  # [0.11.0](https://github.com/kapetacom/local-cluster-service/compare/v0.10.1...v0.11.0) (2023-07-31)
2
9
 
3
10
 
@@ -2,11 +2,13 @@ import { InstanceInfo, LogEntry } from './types';
2
2
  export declare class InstanceManager {
3
3
  private _interval;
4
4
  private readonly _instances;
5
+ private readonly instanceLocks;
5
6
  constructor();
6
7
  private checkInstancesLater;
7
8
  getInstances(): InstanceInfo[];
8
9
  getInstancesForPlan(systemId: string): InstanceInfo[];
9
10
  getInstance(systemId: string, instanceId: string): InstanceInfo | undefined;
11
+ private exclusive;
10
12
  getLogs(systemId: string, instanceId: string): Promise<LogEntry[]>;
11
13
  saveInternalInstance(instance: InstanceInfo): Promise<InstanceInfo>;
12
14
  /**
@@ -15,9 +17,10 @@ export declare class InstanceManager {
15
17
  */
16
18
  registerInstanceFromSDK(systemId: string, instanceId: string, info: Omit<InstanceInfo, 'systemId' | 'instanceId'>): Promise<InstanceInfo | undefined>;
17
19
  private getHealthUrl;
18
- markAsStopped(systemId: string, instanceId: string): void;
20
+ markAsStopped(systemId: string, instanceId: string): Promise<void>;
19
21
  startAllForPlan(systemId: string): Promise<InstanceInfo[]>;
20
22
  stop(systemId: string, instanceId: string): Promise<void>;
23
+ private stopInner;
21
24
  stopAllForPlan(systemId: string): Promise<void>;
22
25
  start(systemId: string, instanceId: string): Promise<InstanceInfo>;
23
26
  restart(systemId: string, instanceId: string): Promise<InstanceInfo>;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.instanceManager = exports.InstanceManager = void 0;
7
7
  const lodash_1 = __importDefault(require("lodash"));
8
8
  const request_1 = __importDefault(require("request"));
9
+ const async_lock_1 = __importDefault(require("async-lock"));
9
10
  const BlockInstanceRunner_1 = require("./utils/BlockInstanceRunner");
10
11
  const storageService_1 = require("./storageService");
11
12
  const socketManager_1 = require("./socketManager");
@@ -25,6 +26,7 @@ const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs -
25
26
  class InstanceManager {
26
27
  _interval = undefined;
27
28
  _instances = [];
29
+ instanceLocks = new async_lock_1.default();
28
30
  constructor() {
29
31
  this._instances = storageService_1.storageService.section('instances', []);
30
32
  // We need to wait a bit before running the first check
@@ -56,6 +58,11 @@ class InstanceManager {
56
58
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
57
59
  return this._instances.find((i) => i.systemId === systemId && i.instanceId === instanceId);
58
60
  }
61
+ async exclusive(systemId, instanceId, fn) {
62
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
63
+ const key = `${systemId}/${instanceId}`;
64
+ return this.instanceLocks.acquire(key, fn);
65
+ }
59
66
  async getLogs(systemId, instanceId) {
60
67
  const instance = this.getInstance(systemId, instanceId);
61
68
  if (!instance) {
@@ -111,57 +118,59 @@ class InstanceManager {
111
118
  * which self-registers with the cluster service locally on startup.
112
119
  */
113
120
  async registerInstanceFromSDK(systemId, instanceId, info) {
114
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
115
- let instance = this.getInstance(systemId, instanceId);
116
- //Get target address
117
- const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
118
- const healthUrl = this.getHealthUrl(info, address);
119
- if (instance) {
120
- if (instance.status === types_1.InstanceStatus.STOPPING && instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
121
- //If instance is stopping do not interfere
122
- return;
123
- }
124
- if (info.owner === types_1.InstanceOwner.EXTERNAL) {
125
- //If instance was started externally - then we want to replace the internal instance with that
126
- if (instance.owner === types_1.InstanceOwner.INTERNAL &&
127
- (instance.status === types_1.InstanceStatus.READY ||
128
- instance.status === types_1.InstanceStatus.STARTING ||
129
- instance.status === types_1.InstanceStatus.UNHEALTHY)) {
130
- throw new Error(`Instance ${instanceId} is already running`);
121
+ return this.exclusive(systemId, instanceId, async () => {
122
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
123
+ let instance = this.getInstance(systemId, instanceId);
124
+ //Get target address
125
+ const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
126
+ const healthUrl = this.getHealthUrl(info, address);
127
+ if (instance) {
128
+ if (instance.status === types_1.InstanceStatus.STOPPING && instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
129
+ //If instance is stopping do not interfere
130
+ return;
131
131
  }
132
- instance.desiredStatus = info.desiredStatus;
133
- instance.owner = info.owner;
134
- instance.status = types_1.InstanceStatus.STARTING;
135
- instance.startedAt = Date.now();
136
- }
137
- instance.pid = info.pid;
138
- instance.address = address;
139
- if (info.type) {
140
- instance.type = info.type;
132
+ if (info.owner === types_1.InstanceOwner.EXTERNAL) {
133
+ //If instance was started externally - then we want to replace the internal instance with that
134
+ if (instance.owner === types_1.InstanceOwner.INTERNAL &&
135
+ (instance.status === types_1.InstanceStatus.READY ||
136
+ instance.status === types_1.InstanceStatus.STARTING ||
137
+ instance.status === types_1.InstanceStatus.UNHEALTHY)) {
138
+ throw new Error(`Instance ${instanceId} is already running`);
139
+ }
140
+ instance.desiredStatus = info.desiredStatus;
141
+ instance.owner = info.owner;
142
+ instance.status = types_1.InstanceStatus.STARTING;
143
+ instance.startedAt = Date.now();
144
+ }
145
+ instance.pid = info.pid;
146
+ instance.address = address;
147
+ if (info.type) {
148
+ instance.type = info.type;
149
+ }
150
+ if (healthUrl) {
151
+ instance.health = healthUrl;
152
+ }
153
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
141
154
  }
142
- if (healthUrl) {
143
- instance.health = healthUrl;
155
+ else {
156
+ //If instance was not found - then we're receiving an externally started instance
157
+ instance = {
158
+ ...info,
159
+ systemId,
160
+ instanceId,
161
+ status: types_1.InstanceStatus.STARTING,
162
+ startedAt: Date.now(),
163
+ desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
164
+ owner: types_1.InstanceOwner.EXTERNAL,
165
+ health: healthUrl,
166
+ address,
167
+ };
168
+ this._instances.push(instance);
169
+ this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
144
170
  }
145
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
146
- }
147
- else {
148
- //If instance was not found - then we're receiving an externally started instance
149
- instance = {
150
- ...info,
151
- systemId,
152
- instanceId,
153
- status: types_1.InstanceStatus.STARTING,
154
- startedAt: Date.now(),
155
- desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
156
- owner: types_1.InstanceOwner.EXTERNAL,
157
- health: healthUrl,
158
- address,
159
- };
160
- this._instances.push(instance);
161
- this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
162
- }
163
- this.save();
164
- return instance;
171
+ this.save();
172
+ return instance;
173
+ });
165
174
  }
166
175
  getHealthUrl(info, address) {
167
176
  let healthUrl = null;
@@ -175,15 +184,17 @@ class InstanceManager {
175
184
  return healthUrl;
176
185
  }
177
186
  markAsStopped(systemId, instanceId) {
178
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
179
- const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
180
- if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
181
- instance.status = types_1.InstanceStatus.STOPPED;
182
- instance.pid = null;
183
- instance.health = null;
184
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
185
- this.save();
186
- }
187
+ return this.exclusive(systemId, instanceId, async () => {
188
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
189
+ const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
190
+ if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
191
+ instance.status = types_1.InstanceStatus.STOPPED;
192
+ instance.pid = null;
193
+ instance.health = null;
194
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
195
+ this.save();
196
+ }
197
+ });
187
198
  }
188
199
  async startAllForPlan(systemId) {
189
200
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
@@ -212,54 +223,60 @@ class InstanceManager {
212
223
  return settled.map((p) => (p.status === 'fulfilled' ? p.value : null)).filter((p) => !!p);
213
224
  }
214
225
  async stop(systemId, instanceId) {
215
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
216
- const instance = this.getInstance(systemId, instanceId);
217
- if (!instance) {
218
- return;
219
- }
220
- if (instance.status === types_1.InstanceStatus.STOPPED) {
221
- return;
222
- }
223
- if (instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
224
- instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
225
- }
226
- instance.status = types_1.InstanceStatus.STOPPING;
227
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
228
- console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
229
- this.save();
230
- try {
231
- if (instance.type === 'docker') {
232
- const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
233
- const container = await containerManager_1.containerManager.getContainerByName(containerName);
234
- if (container) {
235
- try {
236
- await container.stop();
237
- instance.status = types_1.InstanceStatus.STOPPED;
238
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
239
- this.save();
226
+ return this.stopInner(systemId, instanceId, true);
227
+ }
228
+ async stopInner(systemId, instanceId, changeDesired = false) {
229
+ return this.exclusive(systemId, instanceId, async () => {
230
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
231
+ const instance = this.getInstance(systemId, instanceId);
232
+ if (!instance) {
233
+ return;
234
+ }
235
+ if (instance.status === types_1.InstanceStatus.STOPPED) {
236
+ return;
237
+ }
238
+ if (changeDesired &&
239
+ instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
240
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
241
+ }
242
+ instance.status = types_1.InstanceStatus.STOPPING;
243
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
244
+ console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
245
+ this.save();
246
+ try {
247
+ if (instance.type === 'docker') {
248
+ const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
249
+ const container = await containerManager_1.containerManager.getContainerByName(containerName);
250
+ if (container) {
251
+ try {
252
+ await container.stop();
253
+ instance.status = types_1.InstanceStatus.STOPPED;
254
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
255
+ this.save();
256
+ }
257
+ catch (e) {
258
+ console.error('Failed to stop container', e);
259
+ }
240
260
  }
241
- catch (e) {
242
- console.error('Failed to stop container', e);
261
+ else {
262
+ console.warn('Container not found', containerName);
243
263
  }
264
+ return;
244
265
  }
245
- else {
246
- console.warn('Container not found', containerName);
266
+ if (!instance.pid) {
267
+ instance.status = types_1.InstanceStatus.STOPPED;
268
+ this.save();
269
+ return;
247
270
  }
248
- return;
249
- }
250
- if (!instance.pid) {
271
+ process.kill(instance.pid, 'SIGTERM');
251
272
  instance.status = types_1.InstanceStatus.STOPPED;
273
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
252
274
  this.save();
253
- return;
254
275
  }
255
- process.kill(instance.pid, 'SIGTERM');
256
- instance.status = types_1.InstanceStatus.STOPPED;
257
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
258
- this.save();
259
- }
260
- catch (e) {
261
- console.error('Failed to stop process', e);
262
- }
276
+ catch (e) {
277
+ console.error('Failed to stop process', e);
278
+ }
279
+ });
263
280
  }
264
281
  async stopAllForPlan(systemId) {
265
282
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
@@ -267,104 +284,111 @@ class InstanceManager {
267
284
  return this.stopInstances(instancesForPlan);
268
285
  }
269
286
  async start(systemId, instanceId) {
270
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
271
- const plan = await assetManager_1.assetManager.getPlan(systemId, true);
272
- if (!plan) {
273
- throw new Error('Plan not found: ' + systemId);
274
- }
275
- const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
276
- if (!blockInstance) {
277
- throw new Error('Block instance not found: ' + instanceId);
278
- }
279
- const blockRef = (0, utils_1.normalizeKapetaUri)(blockInstance.block.ref);
280
- const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
281
- if (!blockAsset) {
282
- throw new Error('Block not found: ' + blockRef);
283
- }
284
- const existingInstance = this.getInstance(systemId, instanceId);
285
- if (existingInstance) {
286
- if (existingInstance.status === types_1.InstanceStatus.READY) {
287
- // Instance is already running
288
- return existingInstance;
287
+ return this.exclusive(systemId, instanceId, async () => {
288
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
289
+ const plan = await assetManager_1.assetManager.getPlan(systemId, true);
290
+ if (!plan) {
291
+ throw new Error('Plan not found: ' + systemId);
289
292
  }
290
- if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
291
- existingInstance.status === types_1.InstanceStatus.STARTING) {
292
- // Internal instance is already starting - don't start it again
293
- return existingInstance;
293
+ const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
294
+ if (!blockInstance) {
295
+ throw new Error('Block instance not found: ' + instanceId);
294
296
  }
295
- if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
296
- existingInstance.status === types_1.InstanceStatus.STARTING) {
297
- // External instance is already starting - don't start it again
298
- return existingInstance;
297
+ const blockRef = (0, utils_1.normalizeKapetaUri)(blockInstance.block.ref);
298
+ const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
299
+ if (!blockAsset) {
300
+ throw new Error('Block not found: ' + blockRef);
299
301
  }
300
- }
301
- let instance = {
302
- systemId,
303
- instanceId,
304
- ref: blockRef,
305
- name: blockAsset.data.metadata.name,
306
- desiredStatus: types_1.DesiredInstanceStatus.RUN,
307
- owner: types_1.InstanceOwner.INTERNAL,
308
- type: existingInstance?.type ?? types_1.InstanceType.UNKNOWN,
309
- status: types_1.InstanceStatus.STARTING,
310
- startedAt: Date.now(),
311
- };
312
- console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
313
- // Save the instance before starting it, so that we can track the status
314
- await this.saveInternalInstance(instance);
315
- if (existingInstance) {
316
- // Check if the instance is already running - but after we've commmuicated the desired status
317
- const currentStatus = await this.requestInstanceStatus(existingInstance);
318
- if (currentStatus === types_1.InstanceStatus.READY) {
319
- // Instance is already running
320
- return existingInstance;
302
+ const existingInstance = this.getInstance(systemId, instanceId);
303
+ if (existingInstance) {
304
+ if (existingInstance.status === types_1.InstanceStatus.READY) {
305
+ // Instance is already running
306
+ return existingInstance;
307
+ }
308
+ if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
309
+ existingInstance.status === types_1.InstanceStatus.STARTING) {
310
+ // Internal instance is already starting - don't start it again
311
+ return existingInstance;
312
+ }
313
+ if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
314
+ existingInstance.status === types_1.InstanceStatus.STARTING) {
315
+ // External instance is already starting - don't start it again
316
+ return existingInstance;
317
+ }
321
318
  }
322
- }
323
- const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
324
- const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
325
- const startTime = Date.now();
326
- try {
327
- const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
328
- instance.status = types_1.InstanceStatus.READY;
329
- return this.saveInternalInstance({
330
- ...instance,
331
- type: processInfo.type,
332
- pid: processInfo.pid ?? -1,
333
- health: null,
334
- portType: processInfo.portType,
335
- status: types_1.InstanceStatus.READY,
336
- });
337
- }
338
- catch (e) {
339
- console.warn('Failed to start instance', e);
340
- const logs = [
341
- {
342
- source: 'stdout',
343
- level: 'ERROR',
344
- message: e.message,
345
- time: Date.now(),
346
- },
347
- ];
348
- const out = await this.saveInternalInstance({
349
- ...instance,
350
- type: types_1.InstanceType.LOCAL,
351
- pid: null,
352
- health: null,
353
- portType: DEFAULT_HEALTH_PORT_TYPE,
354
- status: types_1.InstanceStatus.FAILED,
355
- });
356
- this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
357
- this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
358
- error: `Failed to start instance: ${e.message}`,
359
- status: EVENT_INSTANCE_EXITED,
360
- instanceId: blockInstance.id,
361
- });
362
- return out;
363
- }
319
+ let instance = {
320
+ systemId,
321
+ instanceId,
322
+ ref: blockRef,
323
+ name: blockAsset.data.metadata.name,
324
+ desiredStatus: types_1.DesiredInstanceStatus.RUN,
325
+ owner: types_1.InstanceOwner.INTERNAL,
326
+ type: existingInstance?.type ?? types_1.InstanceType.UNKNOWN,
327
+ status: types_1.InstanceStatus.STARTING,
328
+ startedAt: Date.now(),
329
+ };
330
+ console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
331
+ // Save the instance before starting it, so that we can track the status
332
+ await this.saveInternalInstance(instance);
333
+ if (existingInstance) {
334
+ // Check if the instance is already running - but after we've commmuicated the desired status
335
+ const currentStatus = await this.requestInstanceStatus(existingInstance);
336
+ if (currentStatus === types_1.InstanceStatus.READY) {
337
+ // Instance is already running
338
+ return existingInstance;
339
+ }
340
+ }
341
+ const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
342
+ const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
343
+ const startTime = Date.now();
344
+ try {
345
+ const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
346
+ instance.status = types_1.InstanceStatus.READY;
347
+ return this.saveInternalInstance({
348
+ ...instance,
349
+ type: processInfo.type,
350
+ pid: processInfo.pid ?? -1,
351
+ health: null,
352
+ portType: processInfo.portType,
353
+ status: types_1.InstanceStatus.READY,
354
+ });
355
+ }
356
+ catch (e) {
357
+ console.warn('Failed to start instance', e);
358
+ const logs = [
359
+ {
360
+ source: 'stdout',
361
+ level: 'ERROR',
362
+ message: e.message,
363
+ time: Date.now(),
364
+ },
365
+ ];
366
+ const out = await this.saveInternalInstance({
367
+ ...instance,
368
+ type: types_1.InstanceType.LOCAL,
369
+ pid: null,
370
+ health: null,
371
+ portType: DEFAULT_HEALTH_PORT_TYPE,
372
+ status: types_1.InstanceStatus.FAILED,
373
+ });
374
+ this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
375
+ this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
376
+ error: `Failed to start instance: ${e.message}`,
377
+ status: EVENT_INSTANCE_EXITED,
378
+ instanceId: blockInstance.id,
379
+ });
380
+ return out;
381
+ }
382
+ });
364
383
  }
365
384
  async restart(systemId, instanceId) {
366
385
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
367
- await this.stop(systemId, instanceId);
386
+ await this.stopInner(systemId, instanceId);
387
+ const existingInstance = this.getInstance(systemId, instanceId);
388
+ if (existingInstance?.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
389
+ // Internal instance was marked as stopped - abort restart
390
+ return existingInstance;
391
+ }
368
392
  return this.start(systemId, instanceId);
369
393
  }
370
394
  async stopAll() {
@@ -391,7 +415,7 @@ class InstanceManager {
391
415
  const all = [...this._instances];
392
416
  while (all.length > 0) {
393
417
  // Check a few instances at a time - docker doesn't like too many concurrent requests
394
- const chunk = all.splice(0, 20);
418
+ const chunk = all.splice(0, 30);
395
419
  const promises = chunk.map(async (instance) => {
396
420
  if (!instance.systemId) {
397
421
  return;
@@ -451,7 +475,7 @@ class InstanceManager {
451
475
  [types_1.InstanceStatus.READY, types_1.InstanceStatus.STARTING, types_1.InstanceStatus.UNHEALTHY].includes(newStatus)) {
452
476
  //If the instance is running but we want it to stop, stop it
453
477
  try {
454
- await this.stop(instance.systemId, instance.instanceId);
478
+ await this.stopInner(instance.systemId, instance.instanceId);
455
479
  }
456
480
  catch (e) {
457
481
  console.warn('Failed to stop instance', instance.systemId, instance.instanceId, e);
@@ -2,11 +2,13 @@ import { InstanceInfo, LogEntry } from './types';
2
2
  export declare class InstanceManager {
3
3
  private _interval;
4
4
  private readonly _instances;
5
+ private readonly instanceLocks;
5
6
  constructor();
6
7
  private checkInstancesLater;
7
8
  getInstances(): InstanceInfo[];
8
9
  getInstancesForPlan(systemId: string): InstanceInfo[];
9
10
  getInstance(systemId: string, instanceId: string): InstanceInfo | undefined;
11
+ private exclusive;
10
12
  getLogs(systemId: string, instanceId: string): Promise<LogEntry[]>;
11
13
  saveInternalInstance(instance: InstanceInfo): Promise<InstanceInfo>;
12
14
  /**
@@ -15,9 +17,10 @@ export declare class InstanceManager {
15
17
  */
16
18
  registerInstanceFromSDK(systemId: string, instanceId: string, info: Omit<InstanceInfo, 'systemId' | 'instanceId'>): Promise<InstanceInfo | undefined>;
17
19
  private getHealthUrl;
18
- markAsStopped(systemId: string, instanceId: string): void;
20
+ markAsStopped(systemId: string, instanceId: string): Promise<void>;
19
21
  startAllForPlan(systemId: string): Promise<InstanceInfo[]>;
20
22
  stop(systemId: string, instanceId: string): Promise<void>;
23
+ private stopInner;
21
24
  stopAllForPlan(systemId: string): Promise<void>;
22
25
  start(systemId: string, instanceId: string): Promise<InstanceInfo>;
23
26
  restart(systemId: string, instanceId: string): Promise<InstanceInfo>;