@abtnode/core 1.16.52-beta-20251003-083412-fdfc4e36 → 1.16.52-beta-20251005-235515-42ad5caf

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.
@@ -73,6 +73,7 @@ const { encrypt } = require('@blocklet/sdk/lib/security');
73
73
  const { generateRandomString } = require('@abtnode/models/lib/util');
74
74
  const { getComponentApiKey, getBlockletLogos } = require('@abtnode/util/lib/blocklet');
75
75
  const { getEmailServiceProvider, emailConfigSchema } = require('@abtnode/auth/lib/email');
76
+ const { Joi } = require('@arcblock/validator');
76
77
 
77
78
  const {
78
79
  BlockletStatus,
@@ -134,6 +135,9 @@ const {
134
135
  refresh: refreshAccessibleExternalNodeIp,
135
136
  getFromCache: getAccessibleExternalNodeIp,
136
137
  } = require('../../util/get-accessible-external-node-ip');
138
+ const { blueGreenStartBlocklet } = require('./helper/blue-green-start-blocklet.js');
139
+ const { blueGreenUpgradeBlocklet } = require('./helper/blue-green-upgrade-blocklet.js');
140
+
137
141
  const {
138
142
  getAppSystemEnvironments,
139
143
  getComponentSystemEnvironments,
@@ -175,6 +179,7 @@ const {
175
179
  getPackConfig,
176
180
  copyPackImages,
177
181
  filterRequiredComponents,
182
+ getHookArgs,
178
183
  } = require('../../util/blocklet');
179
184
  const states = require('../../states');
180
185
  const BaseBlockletManager = require('./base');
@@ -246,6 +251,7 @@ const { blockletThemeSchema } = require('../../validators/theme');
246
251
  const { removeDockerNetwork } = require('../../util/docker/docker-network.js');
247
252
  const parseDockerName = require('../../util/docker/parse-docker-name.js');
248
253
  const { verifyAigneConfig, decryptValue } = require('../../util/aigne-verify');
254
+ const { blueGreenGetComponentIds } = require('./helper/blue-green-get-componentids.js');
249
255
 
250
256
  const { formatEnvironments, getBlockletMeta, validateOwner, isCLI } = util;
251
257
 
@@ -271,16 +277,6 @@ const USER_PROFILE_SYNC_FIELDS = [
271
277
  'phoneVerified',
272
278
  ];
273
279
 
274
- const getHookArgs = (blocklet) => ({
275
- output: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '' : path.join(blocklet.env.logsDir, 'output.log'),
276
- error: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '' : path.join(blocklet.env.logsDir, 'error.log'),
277
- timeout:
278
- Math.max(
279
- get(blocklet, 'meta.timeout.script', 120),
280
- ...(blocklet?.children || []).map((child) => child.meta?.timeout?.script || 0)
281
- ) * 1000,
282
- });
283
-
284
280
  const pm2StatusMap = {
285
281
  online: BlockletStatus.running,
286
282
  stop: BlockletStatus.stopped,
@@ -730,7 +726,8 @@ class DiskBlockletManager extends BaseBlockletManager {
730
726
  }
731
727
 
732
728
  async checkComponentsForUpdates({ did }) {
733
- await this.checkControllerStatus(await this.ensureBlocklet(did), 'upgrade');
729
+ const blocklet = await this.ensureBlocklet(did);
730
+ await this.checkControllerStatus(blocklet, 'upgrade');
734
731
  return UpgradeComponents.check({ did, states });
735
732
  }
736
733
 
@@ -876,6 +873,12 @@ class DiskBlockletManager extends BaseBlockletManager {
876
873
  ) {
877
874
  const operator = _operator || context?.user?.did;
878
875
 
876
+ try {
877
+ await this.deleteProcess({ did, componentDids, isGreen: true });
878
+ } catch {
879
+ // do nothing
880
+ }
881
+
879
882
  logger.info('start blocklet', {
880
883
  did: did || inputBlocklet.meta.did,
881
884
  componentDids,
@@ -1047,7 +1050,10 @@ class DiskBlockletManager extends BaseBlockletManager {
1047
1050
  });
1048
1051
 
1049
1052
  // check blocklet healthy
1050
- const { startTimeout, minConsecutiveTime } = getHealthyCheckTimeout(blocklet, { checkHealthImmediately });
1053
+ const { startTimeout, minConsecutiveTime } = getHealthyCheckTimeout(blocklet, {
1054
+ checkHealthImmediately,
1055
+ componentDids,
1056
+ });
1051
1057
  const params = {
1052
1058
  did,
1053
1059
  context,
@@ -1117,7 +1123,11 @@ class DiskBlockletManager extends BaseBlockletManager {
1117
1123
  const { processId } = blocklet.env;
1118
1124
 
1119
1125
  if (updateStatus) {
1120
- const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, { componentDids, operator });
1126
+ const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, {
1127
+ componentDids,
1128
+ operator,
1129
+ isGreenAndBlue: true,
1130
+ });
1121
1131
  blocklet.status = BlockletStatus.stopping;
1122
1132
  this.emit(BlockletEvents.statusChange, doc);
1123
1133
  }
@@ -1159,11 +1169,16 @@ class DiskBlockletManager extends BaseBlockletManager {
1159
1169
  });
1160
1170
  },
1161
1171
  componentDids,
1172
+ isStopGreenAndBlue: true,
1162
1173
  });
1163
1174
  } catch (error) {
1164
1175
  logger.error('Failed to stop blocklet', { error, did });
1165
1176
  if (updateStatus) {
1166
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids, operator });
1177
+ const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, {
1178
+ componentDids,
1179
+ operator,
1180
+ isGreenAndBlue: true,
1181
+ });
1167
1182
  this.emit(BlockletEvents.statusChange, res);
1168
1183
  }
1169
1184
  throw error;
@@ -1174,7 +1189,11 @@ class DiskBlockletManager extends BaseBlockletManager {
1174
1189
  launcher.notifyBlockletStopped(blocklet);
1175
1190
 
1176
1191
  if (updateStatus) {
1177
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, operator });
1192
+ const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
1193
+ componentDids,
1194
+ operator,
1195
+ isGreenAndBlue: true,
1196
+ });
1178
1197
  // send notification to websocket channel
1179
1198
  this.emit(BlockletEvents.statusChange, res);
1180
1199
 
@@ -1286,10 +1305,7 @@ class DiskBlockletManager extends BaseBlockletManager {
1286
1305
  logger.info('restart blocklet', { did, operator });
1287
1306
  const blocklet = await this.getBlocklet(did);
1288
1307
  await this.checkControllerStatus(blocklet, 'restart');
1289
- await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, {
1290
- componentDids,
1291
- operator,
1292
- });
1308
+
1293
1309
  const result = await states.blocklet.getBlocklet(did);
1294
1310
  this.emit(BlockletEvents.statusChange, result);
1295
1311
 
@@ -1302,12 +1318,9 @@ class DiskBlockletManager extends BaseBlockletManager {
1302
1318
  operator,
1303
1319
  context,
1304
1320
  });
1305
- ticket.on('failed', async (err) => {
1321
+ ticket.on('failed', (err) => {
1306
1322
  logger.error('failed to restart blocklet', { did, error: err });
1307
1323
 
1308
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, operator });
1309
- this.emit(BlockletEvents.statusChange, state);
1310
-
1311
1324
  const description = `${getComponentNamesWithVersion(result, componentDids)} restart failed for ${getDisplayName(
1312
1325
  result
1313
1326
  )}: ${err.message || 'queue exception'}`;
@@ -1640,20 +1653,23 @@ class DiskBlockletManager extends BaseBlockletManager {
1640
1653
 
1641
1654
  const { name, did, version } = blocklet.meta;
1642
1655
 
1643
- if (![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.status)) {
1656
+ if (
1657
+ ![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.greenStatus) &&
1658
+ ![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.status)
1659
+ ) {
1644
1660
  throw new Error(`Can not cancel blocklet that status is ${fromBlockletStatus(blocklet.status)}`);
1645
1661
  }
1646
1662
 
1647
1663
  const job = await this.installQueue.get(did);
1648
1664
 
1649
1665
  // cancel job
1650
- if (blocklet.status === BlockletStatus.downloading) {
1666
+ if (blocklet.greenStatus === BlockletStatus.downloading || blocklet.status === BlockletStatus.downloading) {
1651
1667
  try {
1652
1668
  await this.blockletDownloader.cancelDownload(blocklet.meta.did);
1653
1669
  } catch (error) {
1654
1670
  logger.error('failed to exec blockletDownloader.download', { did: blocklet.meta.did, error });
1655
1671
  }
1656
- } else if (blocklet.status === BlockletStatus.waiting) {
1672
+ } else if (blocklet.greenStatus === BlockletStatus.waiting || blocklet.status === BlockletStatus.waiting) {
1657
1673
  try {
1658
1674
  await this.installQueue.cancel(blocklet.meta.did);
1659
1675
  } catch (error) {
@@ -1670,7 +1686,7 @@ class DiskBlockletManager extends BaseBlockletManager {
1670
1686
  if (data) {
1671
1687
  const { action, oldBlocklet } = data;
1672
1688
  await this._rollback(action, did, oldBlocklet);
1673
- await this._rollbackCache.remove({ did });
1689
+ await this._rollbackCache.remove({ did: inputDid });
1674
1690
  } else {
1675
1691
  throw new Error(`Cannot find rollback data in queue or backup file of blocklet ${inputDid}`);
1676
1692
  }
@@ -1686,7 +1702,10 @@ class DiskBlockletManager extends BaseBlockletManager {
1686
1702
  if (blocklet) {
1687
1703
  const componentDids = [];
1688
1704
  forEachComponentV2Sync(blocklet, (x) => {
1689
- if ([BlockletStatus.waiting, BlockletStatus.downloading].includes(x.status)) {
1705
+ if (
1706
+ [BlockletStatus.waiting, BlockletStatus.downloading].includes(x.status) ||
1707
+ [BlockletStatus.waiting, BlockletStatus.downloading].includes(x.greenStatus)
1708
+ ) {
1690
1709
  componentDids.push(x.meta.did);
1691
1710
  }
1692
1711
  });
@@ -1712,23 +1731,23 @@ class DiskBlockletManager extends BaseBlockletManager {
1712
1731
  * @param {*} context
1713
1732
  * @returns
1714
1733
  */
1715
- async deleteProcess({ did, componentDids, shouldUpdateBlockletStatus = true }, context) {
1734
+ deleteProcess = async ({ did, componentDids, shouldUpdateBlockletStatus = true, isGreen = false }, context) => {
1716
1735
  const blocklet = await this.getBlocklet(did);
1717
1736
 
1718
1737
  logger.info('delete blocklet process', { did, componentDids });
1719
1738
 
1720
- await deleteBlockletProcess(blocklet, { ...context, componentDids });
1739
+ await deleteBlockletProcess(blocklet, { ...context, componentDids, isGreen });
1721
1740
  logger.info('blocklet process deleted successfully', { did, componentDids });
1722
1741
 
1723
1742
  // 有些情况不需要更新 blocklet 状态, 比如下载完成,安装之前清理 process 时, 不需要更新 blocklet 状态
1724
1743
  if (shouldUpdateBlockletStatus) {
1725
- const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
1744
+ const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids, isGreen });
1726
1745
  logger.info('blocklet status updated to stopped after deleted processes', { did, componentDids });
1727
1746
  return result;
1728
1747
  }
1729
1748
 
1730
1749
  return blocklet;
1731
- }
1750
+ };
1732
1751
 
1733
1752
  // Get blocklet by blockletDid or appDid
1734
1753
  async detail(
@@ -2907,7 +2926,7 @@ class DiskBlockletManager extends BaseBlockletManager {
2907
2926
  return summary;
2908
2927
  }
2909
2928
 
2910
- async updateBlockletSettings({ did, enableSessionHardening, invite, gateway, aigne }, context) {
2929
+ async updateBlockletSettings({ did, enableSessionHardening, invite, gateway, aigne, org }, context) {
2911
2930
  const params = {};
2912
2931
  if (!isNil(enableSessionHardening)) {
2913
2932
  params.enableSessionHardening = enableSessionHardening;
@@ -2939,6 +2958,20 @@ class DiskBlockletManager extends BaseBlockletManager {
2939
2958
  url: getOriginUrl(aigne.url),
2940
2959
  };
2941
2960
  }
2961
+
2962
+ if (!isNil(org)) {
2963
+ const ORG_SCHEMA = Joi.object({
2964
+ enabled: Joi.boolean().required(),
2965
+ maxMemberPerOrg: Joi.number().required().min(2).default(100),
2966
+ maxOrgPerUser: Joi.number().required().min(1).default(10),
2967
+ });
2968
+ const { error } = ORG_SCHEMA.validate(org);
2969
+ if (error) {
2970
+ throw new CustomError(400, error.message);
2971
+ }
2972
+ params.org = org;
2973
+ }
2974
+
2942
2975
  const keys = Object.keys(params);
2943
2976
  if (!keys.length) {
2944
2977
  throw new Error('No settings to update');
@@ -3208,7 +3241,22 @@ class DiskBlockletManager extends BaseBlockletManager {
3208
3241
  return x;
3209
3242
  });
3210
3243
 
3244
+ const blueGreenComponentIds = await blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
3245
+
3211
3246
  try {
3247
+ await Promise.all(
3248
+ blueGreenComponentIds.map(async (item) => {
3249
+ if (item.componentDids.length === 0) {
3250
+ return;
3251
+ }
3252
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
3253
+ componentDids: item.componentDids,
3254
+ isGreen: item.changeToGreen,
3255
+ });
3256
+ })
3257
+ );
3258
+ const state = await states.blocklet.getBlocklet(did);
3259
+ this.emit(BlockletEvents.statusChange, state);
3212
3260
  const { isCancelled } = await this._downloadBlocklet(
3213
3261
  {
3214
3262
  ...blocklet,
@@ -3269,8 +3317,10 @@ class DiskBlockletManager extends BaseBlockletManager {
3269
3317
 
3270
3318
  // rollback on download failed
3271
3319
  await statusLock.acquire('rollback-on-download-failed');
3320
+
3272
3321
  try {
3273
- if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
3322
+ const status = await states.blocklet.getBlockletStatus(did);
3323
+ if ([BlockletStatus.downloading, BlockletStatus.waiting].includes(status)) {
3274
3324
  await this._rollback(postAction, did, oldBlocklet);
3275
3325
  }
3276
3326
  } catch (error) {
@@ -3289,25 +3339,38 @@ class DiskBlockletManager extends BaseBlockletManager {
3289
3339
  // update status
3290
3340
  await statusLock.acquire('download-update-status');
3291
3341
  try {
3292
- if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
3293
- throw new Error('blocklet status changed durning download');
3294
- }
3342
+ await Promise.all(
3343
+ blueGreenComponentIds.map(async (item) => {
3344
+ if (item.componentDids.length === 0) {
3345
+ return;
3346
+ }
3347
+ if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
3348
+ throw new Error('blocklet status changed durning download');
3349
+ }
3295
3350
 
3296
- if (postAction === INSTALL_ACTIONS.INSTALL) {
3297
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, { componentDids });
3298
- this.emit(BlockletEvents.statusChange, state);
3299
- }
3351
+ if (postAction === INSTALL_ACTIONS.INSTALL) {
3352
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, {
3353
+ componentDids: item.componentDids,
3354
+ isGreen: item.changeToGreen,
3355
+ });
3356
+ this.emit(BlockletEvents.statusChange, state);
3357
+ }
3300
3358
 
3301
- if (
3302
- [
3303
- INSTALL_ACTIONS.INSTALL_COMPONENT,
3304
- INSTALL_ACTIONS.UPGRADE_COMPONENT,
3305
- 'upgrade', // for backward compatibility
3306
- ].includes(postAction)
3307
- ) {
3308
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
3309
- this.emit(BlockletEvents.statusChange, state);
3310
- }
3359
+ if (
3360
+ [
3361
+ INSTALL_ACTIONS.INSTALL_COMPONENT,
3362
+ INSTALL_ACTIONS.UPGRADE_COMPONENT,
3363
+ 'upgrade', // for backward compatibility
3364
+ ].includes(postAction)
3365
+ ) {
3366
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, {
3367
+ componentDids: item.componentDids,
3368
+ isGreen: item.changeToGreen,
3369
+ });
3370
+ this.emit(BlockletEvents.statusChange, state);
3371
+ }
3372
+ })
3373
+ );
3311
3374
  } catch (error) {
3312
3375
  logger.error(error.message);
3313
3376
  } finally {
@@ -3335,15 +3398,30 @@ class DiskBlockletManager extends BaseBlockletManager {
3335
3398
  logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
3336
3399
 
3337
3400
  try {
3338
- await this._upgradeBlocklet({
3339
- newBlocklet: blocklet,
3340
- oldBlocklet,
3341
- componentDids,
3342
- context,
3343
- action: postAction,
3344
- shouldCleanUploadFile,
3345
- url,
3346
- });
3401
+ if (postAction === INSTALL_ACTIONS.UPGRADE_COMPONENT) {
3402
+ await blueGreenUpgradeBlocklet(
3403
+ {
3404
+ newBlocklet: blocklet,
3405
+ oldBlocklet,
3406
+ componentDids,
3407
+ action: postAction,
3408
+ shouldCleanUploadFile,
3409
+ url,
3410
+ },
3411
+ context,
3412
+ this,
3413
+ states
3414
+ );
3415
+ } else {
3416
+ await this._upgradeBlocklet({
3417
+ newBlocklet: blocklet,
3418
+ oldBlocklet,
3419
+ componentDids,
3420
+ action: postAction,
3421
+ shouldCleanUploadFile,
3422
+ url,
3423
+ });
3424
+ }
3347
3425
 
3348
3426
  const newBlocklet = await this.getBlocklet(did);
3349
3427
 
@@ -3402,11 +3480,13 @@ class DiskBlockletManager extends BaseBlockletManager {
3402
3480
  }
3403
3481
 
3404
3482
  async _onRestart({ did, componentDids, context, operator }) {
3405
- await this.stop({ did, componentDids, updateStatus: false, operator }, context);
3406
- await this.start({ did, componentDids, operator }, context);
3483
+ await blueGreenStartBlocklet({ did, componentDids, operator }, context, this, states);
3407
3484
  }
3408
3485
 
3409
- async _onCheckIfStarted(jobInfo, { throwOnError, skipRunningCheck = false } = {}) {
3486
+ _onCheckIfStarted = async (
3487
+ jobInfo,
3488
+ { throwOnError, skipRunningCheck = false, isGreen = false, needUpdateBlueStatus = true } = {}
3489
+ ) => {
3410
3490
  const startedAt = Date.now();
3411
3491
  const { did, context, minConsecutiveTime = 2000, timeout, componentDids } = jobInfo;
3412
3492
  const blocklet = await this.getBlocklet(did);
@@ -3415,20 +3495,29 @@ class DiskBlockletManager extends BaseBlockletManager {
3415
3495
  const { name } = meta;
3416
3496
 
3417
3497
  const nodeInfo = await states.node.read();
3498
+ const successBlockletIds = new Set();
3418
3499
  try {
3419
3500
  if (!skipRunningCheck) {
3420
3501
  await checkBlockletProcessHealthy(blocklet, {
3421
3502
  minConsecutiveTime,
3422
3503
  timeout,
3423
3504
  componentDids,
3505
+ isGreen,
3424
3506
  enableDocker: nodeInfo.enableDocker,
3425
3507
  setBlockletRunning: async (componentDid) => {
3426
- await states.blocklet.setBlockletStatus(did, BlockletStatus.running, { componentDids: [componentDid] });
3508
+ successBlockletIds.add(componentDid);
3509
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
3510
+ componentDids: [componentDid],
3511
+ isGreen,
3512
+ });
3427
3513
  },
3428
3514
  });
3429
3515
  }
3430
3516
 
3431
- const runningRes = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, { componentDids });
3517
+ const runningRes = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
3518
+ componentDids: successBlockletIds.size > 0 ? Array.from(successBlockletIds) : componentDids,
3519
+ isGreen,
3520
+ });
3432
3521
 
3433
3522
  const res = await this.getBlocklet(did);
3434
3523
 
@@ -3445,8 +3534,30 @@ class DiskBlockletManager extends BaseBlockletManager {
3445
3534
 
3446
3535
  logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
3447
3536
 
3537
+ if (needUpdateBlueStatus) {
3538
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
3539
+ componentDids,
3540
+ isGreen: !isGreen,
3541
+ });
3542
+ try {
3543
+ await this.deleteProcess({ did, componentDids, isGreen: !isGreen }, context);
3544
+ } catch {
3545
+ logger.error('delete process failed', { did, componentDids, isGreen });
3546
+ }
3547
+ }
3548
+
3448
3549
  return runningRes;
3449
3550
  } catch (error) {
3551
+ const errorBlockletIds = [];
3552
+ for (const componentDid of componentDids) {
3553
+ if (!successBlockletIds.has(componentDid)) {
3554
+ errorBlockletIds.push(componentDid);
3555
+ }
3556
+ }
3557
+ if (errorBlockletIds.length === 0) {
3558
+ return this.getBlocklet(did);
3559
+ }
3560
+
3450
3561
  const status = await states.blocklet.getBlockletStatus(did);
3451
3562
  if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
3452
3563
  logger.info(`Check blocklet healthy failing because blocklet is ${fromBlockletStatus(status)}`);
@@ -3455,12 +3566,21 @@ class DiskBlockletManager extends BaseBlockletManager {
3455
3566
 
3456
3567
  logger.error('check blocklet if started failed', { did, name, context, timeout, error });
3457
3568
 
3458
- await this.deleteProcess({ did, componentDids }, context);
3459
- const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
3569
+ await this.deleteProcess({ did, componentDids: errorBlockletIds, isGreen }, context);
3460
3570
 
3461
- const componentNames = getComponentNamesWithVersion(blocklet, componentDids);
3571
+ let doc;
3572
+ if (isGreen) {
3573
+ doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, {
3574
+ componentDids: errorBlockletIds,
3575
+ isGreen,
3576
+ });
3577
+ } else {
3578
+ doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids: errorBlockletIds });
3579
+ }
3580
+
3581
+ const componentNames = getComponentNamesWithVersion(blocklet, errorBlockletIds);
3462
3582
  const description = componentNames
3463
- ? `${getComponentNamesWithVersion(blocklet, componentDids)} start failed for ${getDisplayName(blocklet)}: ${
3583
+ ? `${getComponentNamesWithVersion(blocklet, errorBlockletIds)} start failed for ${getDisplayName(blocklet)}: ${
3464
3584
  error.message
3465
3585
  }`
3466
3586
  : `${blocklet.meta.title} start failed: ${error.message}`;
@@ -3473,7 +3593,11 @@ class DiskBlockletManager extends BaseBlockletManager {
3473
3593
  severity: 'error',
3474
3594
  });
3475
3595
 
3476
- this.emit(BlockletEvents.startFailed, { ...doc, componentDids, error: { message: error.message } });
3596
+ this.emit(BlockletEvents.startFailed, {
3597
+ ...doc,
3598
+ componentDids: errorBlockletIds,
3599
+ error: { message: error.message },
3600
+ });
3477
3601
  this.emit(BlockletEvents.statusChange, { ...doc, error });
3478
3602
 
3479
3603
  if (throwOnError) {
@@ -3482,7 +3606,7 @@ class DiskBlockletManager extends BaseBlockletManager {
3482
3606
 
3483
3607
  return doc;
3484
3608
  }
3485
- }
3609
+ };
3486
3610
 
3487
3611
  /**
3488
3612
  * @param {{
@@ -4401,7 +4525,7 @@ class DiskBlockletManager extends BaseBlockletManager {
4401
4525
  }
4402
4526
  }
4403
4527
 
4404
- async _runMigration({ needRunDocker, did, blocklet, oldBlocklet, componentDids }) {
4528
+ async _runMigration({ parallel, did, blocklet, oldBlocklet, componentDids }) {
4405
4529
  logger.info('start migration on upgrading', { did, componentDids });
4406
4530
  const oldVersions = {};
4407
4531
  forEachComponentV2Sync(oldBlocklet, (b) => {
@@ -4471,8 +4595,8 @@ class DiskBlockletManager extends BaseBlockletManager {
4471
4595
  }
4472
4596
  };
4473
4597
  await forEachComponentV2(blocklet, runMigration, {
4474
- parallel: !needRunDocker,
4475
- concurrencyLimit: needRunDocker ? 1 : 4,
4598
+ parallel,
4599
+ concurrencyLimit: parallel ? 4 : 1,
4476
4600
  });
4477
4601
  logger.info('done migration on upgrading', { did, componentDids });
4478
4602
  }
@@ -4530,7 +4654,7 @@ class DiskBlockletManager extends BaseBlockletManager {
4530
4654
 
4531
4655
  await this._runUserHook('preFlight', blocklet, context);
4532
4656
 
4533
- await this._runMigration({ needRunDocker, did, blocklet, oldBlocklet, componentDids });
4657
+ await this._runMigration({ parallel: !needRunDocker, did, blocklet, oldBlocklet, componentDids });
4534
4658
 
4535
4659
  // handle component status
4536
4660
  const runningDids = [];
@@ -4696,15 +4820,7 @@ class DiskBlockletManager extends BaseBlockletManager {
4696
4820
 
4697
4821
  return this.blockletDownloader.download(blocklet, {
4698
4822
  ...context,
4699
- preDownload: async ({ downloadComponentIds }) => {
4700
- // update children status
4701
- if (downloadComponentIds?.length) {
4702
- const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
4703
- componentDids: downloadComponentIds,
4704
- });
4705
- this.emit(BlockletEvents.statusChange, blocklet1);
4706
- }
4707
- },
4823
+ // preDownload: () => {},
4708
4824
  onProgress: (data) => {
4709
4825
  this.emit(BlockletEvents.downloadBundleProgress, { appDid: appDid || did, meta: { did }, ...data });
4710
4826
  },
@@ -177,7 +177,8 @@ class EnsureBlockletRunning {
177
177
  const { did } = rootBlocklet.meta;
178
178
  if (rootBlocklet.children) {
179
179
  for (const childBlocklet of rootBlocklet.children) {
180
- const isRunning = runningStatuses.includes(childBlocklet.status);
180
+ const isRunning =
181
+ runningStatuses.includes(childBlocklet.status) || childBlocklet.greenStatus === BlockletStatus.running;
181
182
  const isInProgress = inProgressStatuses.includes(childBlocklet.status);
182
183
  if (isRunning || isInProgress) {
183
184
  if (!this.runningBlocklets[did]) {
@@ -207,7 +208,7 @@ class EnsureBlockletRunning {
207
208
  return async () => {
208
209
  if (!shouldCheckHealthy(blocklet)) {
209
210
  // 如果 blocklet 是不需要启动的,并且不是 running,则设置为 running 状态
210
- if (blocklet.status !== BlockletStatus.running) {
211
+ if (blocklet.status !== BlockletStatus.running && blocklet.greenStatus !== BlockletStatus.running) {
211
212
  await this.states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
212
213
  componentDids: [blocklet.meta.did],
213
214
  });
@@ -0,0 +1,59 @@
1
+ const { BlockletStatus } = require('../../../states/blocklet');
2
+ const { forEachBlocklet } = require('../../../util/blocklet');
3
+
4
+ const blueGreenGetComponentIds = async (blocklet, componentDids) => {
5
+ if (!blocklet) {
6
+ return {
7
+ componentDids: componentDids || [],
8
+ changeToGreen: false,
9
+ };
10
+ }
11
+ const componentDidsSet = new Set(componentDids);
12
+ const runningStatuses = new Set([BlockletStatus.running]);
13
+ const greenComponentIds = [];
14
+ const blueComponentIds = [];
15
+
16
+ const activeStatuses = new Set([BlockletStatus.running, BlockletStatus.starting]);
17
+ const children = blocklet.children || [];
18
+ const hasActiveInstance =
19
+ activeStatuses.has(blocklet.status) ||
20
+ activeStatuses.has(blocklet.greenStatus) ||
21
+ children.some((child) => activeStatuses.has(child.status) || activeStatuses.has(child.greenStatus));
22
+
23
+ if (!hasActiveInstance) {
24
+ return [
25
+ {
26
+ componentDids: Array.from(componentDidsSet),
27
+ changeToGreen: false,
28
+ },
29
+ ];
30
+ }
31
+
32
+ await forEachBlocklet(
33
+ blocklet,
34
+ (b) => {
35
+ if (!componentDidsSet.has(b.meta.did)) {
36
+ return;
37
+ }
38
+ if (runningStatuses.has(b.greenStatus)) {
39
+ greenComponentIds.push(b.meta.did);
40
+ } else {
41
+ blueComponentIds.push(b.meta.did);
42
+ }
43
+ },
44
+ { parallel: true, concurrencyLimit: 10 }
45
+ );
46
+
47
+ return [
48
+ {
49
+ componentDids: greenComponentIds,
50
+ changeToGreen: false,
51
+ },
52
+ {
53
+ componentDids: blueComponentIds,
54
+ changeToGreen: true,
55
+ },
56
+ ];
57
+ };
58
+
59
+ module.exports = { blueGreenGetComponentIds };