backend: implement health check for storage providers and integrate into service initialization
This commit is contained in:
parent
da26614033
commit
385dab5710
@ -90,5 +90,11 @@ const configureStorage = (storageMode: string) => {
|
||||
export const initializeEagerServices = async () => {
|
||||
// Force the creation of services that need to be initialized at startup
|
||||
container.get(RecordingService);
|
||||
await container.get(MeetStorageService).initializeGlobalPreferences();
|
||||
|
||||
// Perform comprehensive health checks before initializing other services
|
||||
const storageService = container.get(MeetStorageService);
|
||||
await storageService.checkStartupHealth();
|
||||
|
||||
// Initialize global preferences after health checks pass
|
||||
await storageService.initializeGlobalPreferences();
|
||||
};
|
||||
|
||||
@ -148,4 +148,24 @@ export class ABSStorageProvider implements StorageProvider {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a health check on the Azure Blob Storage provider.
|
||||
*/
|
||||
async checkHealth(): Promise<{ accessible: boolean; bucketExists?: boolean; containerExists?: boolean }> {
|
||||
try {
|
||||
this.logger.debug('Performing ABS storage health check');
|
||||
const healthResult = await this.azureBlobService.checkHealth();
|
||||
return {
|
||||
accessible: healthResult.accessible,
|
||||
containerExists: healthResult.containerExists
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`ABS storage health check failed: ${error}`);
|
||||
return {
|
||||
accessible: false,
|
||||
containerExists: false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,4 +287,26 @@ export class ABSService {
|
||||
|
||||
return `${prefix}/${name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check for Azure Blob Storage service and container accessibility.
|
||||
* Verifies both service connectivity and container existence.
|
||||
*/
|
||||
async checkHealth(): Promise<{ accessible: boolean; containerExists: boolean }> {
|
||||
try {
|
||||
// Check if we can access the container by checking if it exists
|
||||
const exists = await this.containerClient.exists();
|
||||
|
||||
if (exists) {
|
||||
this.logger.verbose(`ABS health check: service accessible and container '${MEET_AZURE_CONTAINER_NAME}' exists`);
|
||||
return { accessible: true, containerExists: true };
|
||||
} else {
|
||||
this.logger.error(`ABS container '${MEET_AZURE_CONTAINER_NAME}' does not exist`);
|
||||
return { accessible: true, containerExists: false };
|
||||
}
|
||||
} catch (error: any) {
|
||||
this.logger.error(`ABS health check failed: ${error.message}`);
|
||||
return { accessible: false, containerExists: false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,4 +137,24 @@ export class S3StorageProvider implements StorageProvider {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a health check on the S3 storage provider.
|
||||
*/
|
||||
async checkHealth(): Promise<{ accessible: boolean; bucketExists?: boolean; containerExists?: boolean }> {
|
||||
try {
|
||||
this.logger.debug('Performing S3 storage health check');
|
||||
const healthResult = await this.s3Service.checkHealth();
|
||||
return {
|
||||
accessible: healthResult.accessible,
|
||||
bucketExists: healthResult.bucketExists
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`S3 storage health check failed: ${error}`);
|
||||
return {
|
||||
accessible: false,
|
||||
bucketExists: false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,6 +233,37 @@ export class S3Service {
|
||||
this.logger.info('S3 client destroyed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check for S3 service and bucket accessibility.
|
||||
* Verifies both service connectivity and bucket existence.
|
||||
*/
|
||||
async checkHealth(): Promise<{ accessible: boolean; bucketExists: boolean }> {
|
||||
try {
|
||||
// Check if we can access the S3 service by listing objects with a small limit
|
||||
await this.run(
|
||||
new ListObjectsV2Command({
|
||||
Bucket: MEET_S3_BUCKET,
|
||||
MaxKeys: 1
|
||||
})
|
||||
);
|
||||
|
||||
// If we reach here, both service and bucket are accessible
|
||||
this.logger.verbose(`S3 health check: service accessible and bucket '${MEET_S3_BUCKET}' exists`);
|
||||
return { accessible: true, bucketExists: true };
|
||||
} catch (error: any) {
|
||||
this.logger.error(`S3 health check failed: ${error.message}`);
|
||||
|
||||
// Check if it's a bucket-specific error
|
||||
if (error.name === 'NoSuchBucket') {
|
||||
this.logger.error(`S3 bucket '${MEET_S3_BUCKET}' does not exist`);
|
||||
return { accessible: true, bucketExists: false };
|
||||
}
|
||||
|
||||
// Service is not accessible
|
||||
return { accessible: false, bucketExists: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the full key for an S3 object by ensuring it includes the specified sub-bucket prefix.
|
||||
* If the provided name already starts with the prefix, it is returned as-is.
|
||||
|
||||
@ -93,6 +93,14 @@ export interface StorageProvider {
|
||||
* @returns A promise that resolves to a readable stream of the object content
|
||||
*/
|
||||
getObjectAsStream(key: string, range?: { start: number; end: number }): Promise<Readable>;
|
||||
|
||||
/**
|
||||
* Performs a health check on the storage provider.
|
||||
* Verifies both service connectivity and container/bucket existence.
|
||||
*
|
||||
* @returns A promise that resolves to an object indicating accessibility and container/bucket existence
|
||||
*/
|
||||
checkHealth(): Promise<{ accessible: boolean; bucketExists?: boolean; containerExists?: boolean }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -67,6 +67,36 @@ export class MeetStorageService<
|
||||
this.keyBuilder = keyBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a health check on the storage system.
|
||||
* Verifies both service connectivity and container/bucket existence.
|
||||
* Terminates the process if storage is not accessible.
|
||||
*/
|
||||
async checkStartupHealth(): Promise<void> {
|
||||
try {
|
||||
this.logger.verbose('Performing storage health check...');
|
||||
|
||||
// Get the underlying storage service to perform health check
|
||||
const isHealthy = await this.storageProvider.checkHealth();
|
||||
|
||||
if (!isHealthy.accessible) {
|
||||
this.logger.error('Storage service is not accessible. Terminating process...');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!isHealthy.bucketExists && !isHealthy.containerExists) {
|
||||
this.logger.error('Storage bucket/container does not exist. Terminating process...');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
this.logger.verbose('Storage health check passed successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Storage health check failed:', error);
|
||||
this.logger.error('Terminating process due to storage health check failure...');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GLOBAL PREFERENCES DOMAIN LOGIC
|
||||
// ==========================================
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user