mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
feature: report performance metrics for files plugin
This commit is contained in:
parent
199eb170f9
commit
7b02bba8df
7 changed files with 131 additions and 13 deletions
|
@ -15,6 +15,9 @@ import { Readable, Transform } from 'stream';
|
|||
import { pipeline } from 'stream/promises';
|
||||
import { promisify } from 'util';
|
||||
import { lastValueFrom, defer } from 'rxjs';
|
||||
import { PerformanceMetricEvent, reportPerformanceMetricEvent } from '@kbn/ebt-tools';
|
||||
import { FilesPlugin } from '../../../plugin';
|
||||
import { FILE_UPLOAD_PERFORMANCE_EVENT_NAME } from '../../../performance';
|
||||
import type { BlobStorageClient } from '../../types';
|
||||
import type { ReadableContentStream } from './content_stream';
|
||||
import { getReadableContentStream, getWritableContentStream } from './content_stream';
|
||||
|
@ -29,8 +32,14 @@ export const BLOB_STORAGE_SYSTEM_INDEX_NAME = '.kibana_blob_storage';
|
|||
|
||||
export const MAX_BLOB_STORE_SIZE_BYTES = 50 * 1024 * 1024 * 1024; // 50 GiB
|
||||
|
||||
interface UploadOptions {
|
||||
transforms?: Transform[];
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export class ElasticsearchBlobStorageClient implements BlobStorageClient {
|
||||
private static defaultSemaphore: Semaphore;
|
||||
|
||||
/**
|
||||
* Call this function once to globally set a concurrent upload limit for
|
||||
* all {@link ElasticsearchBlobStorageClient} instances.
|
||||
|
@ -89,14 +98,14 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient {
|
|||
}
|
||||
});
|
||||
|
||||
public async upload(
|
||||
src: Readable,
|
||||
{ transforms, id }: { transforms?: Transform[]; id?: string } = {}
|
||||
): Promise<{ id: string; size: number }> {
|
||||
public async upload(src: Readable, options: UploadOptions = {}) {
|
||||
const { transforms, id } = options;
|
||||
|
||||
await this.createIndexIfNotExists();
|
||||
|
||||
const processUpload = async () => {
|
||||
try {
|
||||
const analytics = FilesPlugin.getAnalytics();
|
||||
const dest = getWritableContentStream({
|
||||
id,
|
||||
client: this.esClient,
|
||||
|
@ -106,14 +115,32 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient {
|
|||
maxChunkSize: this.chunkSize,
|
||||
},
|
||||
});
|
||||
await pipeline.apply(null, [src, ...(transforms ?? []), dest] as unknown as Parameters<
|
||||
typeof pipeline
|
||||
>);
|
||||
|
||||
return {
|
||||
id: dest.getContentReferenceId()!,
|
||||
size: dest.getBytesWritten(),
|
||||
const start = performance.now();
|
||||
await pipeline([src, ...(transforms ?? []), dest]);
|
||||
const end = performance.now();
|
||||
|
||||
const _id = dest.getContentReferenceId()!;
|
||||
const size = dest.getBytesWritten();
|
||||
|
||||
const perfArgs: PerformanceMetricEvent = {
|
||||
eventName: FILE_UPLOAD_PERFORMANCE_EVENT_NAME,
|
||||
duration: end - start,
|
||||
key1: 'size',
|
||||
value1: size,
|
||||
meta: {
|
||||
datasource: 'es',
|
||||
id: _id,
|
||||
index: this.index,
|
||||
chunkSize: this.chunkSize,
|
||||
},
|
||||
};
|
||||
|
||||
if (analytics) {
|
||||
reportPerformanceMetricEvent(analytics, perfArgs);
|
||||
}
|
||||
|
||||
return { id: _id, size };
|
||||
} catch (e) {
|
||||
this.logger.error(`Could not write chunks to Elasticsearch for id ${id}: ${e}`);
|
||||
throw e;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { AnalyticsServiceStart } from '@kbn/core-analytics-server';
|
||||
import type { JsonValue } from '@kbn/utility-types';
|
||||
import type { Readable, Transform } from 'stream';
|
||||
|
||||
|
@ -29,6 +30,11 @@ export interface UploadOptions {
|
|||
* And so on.
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* Optional analytics service client in order to report performance of upload.
|
||||
*/
|
||||
analytics?: AnalyticsServiceStart;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,7 @@ import cuid from 'cuid';
|
|||
import { type Logger, SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import type { AuditLogger } from '@kbn/security-plugin/server';
|
||||
import type { UsageCounter } from '@kbn/usage-collection-plugin/server';
|
||||
|
||||
import type {
|
||||
File,
|
||||
FileJSON,
|
||||
|
@ -34,6 +35,11 @@ import { createAuditEvent } from '../audit_events';
|
|||
import type { FileClient, CreateArgs, DeleteArgs, P1, ShareArgs } from './types';
|
||||
import { serializeJSON, toJSON } from '../file/to_json';
|
||||
import { createDefaultFileAttributes } from './utils';
|
||||
import {
|
||||
PerfArgs,
|
||||
withReportPerformanceMetric,
|
||||
FILE_DOWNLOAD_PERFORMANCE_EVENT_NAME,
|
||||
} from '../performance';
|
||||
|
||||
export type UploadOptions = Omit<BlobUploadOptions, 'id'>;
|
||||
|
||||
|
@ -219,10 +225,21 @@ export class FileClientImpl implements FileClient {
|
|||
});
|
||||
};
|
||||
|
||||
public download: BlobStorageClient['download'] = (args) => {
|
||||
public download: BlobStorageClient['download'] = async (args) => {
|
||||
this.incrementUsageCounter('DOWNLOAD');
|
||||
try {
|
||||
return this.blobStorageClient.download(args);
|
||||
const perf: PerfArgs = {
|
||||
eventData: {
|
||||
eventName: FILE_DOWNLOAD_PERFORMANCE_EVENT_NAME,
|
||||
key1: 'size',
|
||||
value1: args.size,
|
||||
meta: {
|
||||
id: args.id,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return withReportPerformanceMetric(perf, () => this.blobStorageClient.download(args));
|
||||
} catch (e) {
|
||||
this.incrementUsageCounter('DOWNLOAD_ERROR');
|
||||
throw e;
|
||||
|
|
10
src/plugins/files/server/performance/event_names.ts
Normal file
10
src/plugins/files/server/performance/event_names.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const FILE_DOWNLOAD_PERFORMANCE_EVENT_NAME = 'file-download';
|
||||
export const FILE_UPLOAD_PERFORMANCE_EVENT_NAME = 'file-upload';
|
15
src/plugins/files/server/performance/index.ts
Normal file
15
src/plugins/files/server/performance/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type { PerfArgs } from './report_performance';
|
||||
export { withReportPerformanceMetric } from './report_performance';
|
||||
|
||||
export {
|
||||
FILE_DOWNLOAD_PERFORMANCE_EVENT_NAME,
|
||||
FILE_UPLOAD_PERFORMANCE_EVENT_NAME,
|
||||
} from './event_names';
|
32
src/plugins/files/server/performance/report_performance.ts
Normal file
32
src/plugins/files/server/performance/report_performance.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { PerformanceMetricEvent, reportPerformanceMetricEvent } from '@kbn/ebt-tools';
|
||||
import { Optional } from 'utility-types';
|
||||
import { FilesPlugin } from '../plugin';
|
||||
|
||||
export interface PerfArgs {
|
||||
eventData: Optional<PerformanceMetricEvent, 'duration'>;
|
||||
}
|
||||
|
||||
export async function withReportPerformanceMetric<T>(perfArgs: PerfArgs, cb: () => Promise<T>) {
|
||||
const analytics = FilesPlugin.getAnalytics();
|
||||
|
||||
const start = performance.now();
|
||||
const response = await cb();
|
||||
const end = performance.now();
|
||||
|
||||
if (analytics) {
|
||||
reportPerformanceMetricEvent(analytics, {
|
||||
...perfArgs.eventData,
|
||||
duration: end - start,
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
|
@ -14,6 +14,7 @@ import type {
|
|||
CoreStart,
|
||||
} from '@kbn/core/server';
|
||||
|
||||
import { AnalyticsServiceStart } from '@kbn/core/server';
|
||||
import { PLUGIN_ID } from '../common/constants';
|
||||
import {
|
||||
setFileKindsRegistry,
|
||||
|
@ -35,6 +36,7 @@ import { registerRoutes, registerFileKindRoutes } from './routes';
|
|||
import { Counters, registerUsageCollector } from './usage';
|
||||
|
||||
export class FilesPlugin implements Plugin<FilesSetup, FilesStart, FilesPluginSetupDependencies> {
|
||||
private static analytics?: AnalyticsServiceStart;
|
||||
private readonly logger: Logger;
|
||||
private fileServiceFactory: undefined | FileServiceFactory;
|
||||
private securitySetup: FilesPluginSetupDependencies['security'];
|
||||
|
@ -44,6 +46,14 @@ export class FilesPlugin implements Plugin<FilesSetup, FilesStart, FilesPluginSe
|
|||
this.logger = initializerContext.logger.get();
|
||||
}
|
||||
|
||||
public static getAnalytics() {
|
||||
return this.analytics;
|
||||
}
|
||||
|
||||
private static setAnalytics(analytics: AnalyticsServiceStart) {
|
||||
this.analytics = analytics;
|
||||
}
|
||||
|
||||
public setup(
|
||||
core: CoreSetup,
|
||||
{ security, usageCollection }: FilesPluginSetupDependencies
|
||||
|
@ -89,8 +99,9 @@ export class FilesPlugin implements Plugin<FilesSetup, FilesStart, FilesPluginSe
|
|||
}
|
||||
|
||||
public start(coreStart: CoreStart, { security }: FilesPluginStartDependencies): FilesStart {
|
||||
const { savedObjects } = coreStart;
|
||||
const { savedObjects, analytics } = coreStart;
|
||||
this.securityStart = security;
|
||||
FilesPlugin.setAnalytics(analytics);
|
||||
const esClient = coreStart.elasticsearch.client.asInternalUser;
|
||||
const blobStorageService = new BlobStorageService(
|
||||
esClient,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue