kibana/examples/response_stream/server/routes/single_string_stream.ts
Walter Rafelsberger 0ab24e566c
[ML] AIOps: Use Kibana's http service instead of fetch, fix throttling. (#162335)
- Originally Kibana's `http` service did not support receiving streams,
that's why we used plain `fetch` for this. This has been fixed in
#158678, so this PR updates the streaming helpers to use Kibana's `http`
service from now on.
- The PR also breaks out the response stream code into its own package
and restructures it to separate client and server side code. This brings
down the `aiops` bundle size by `~300KB`! 🥳
- The approach to client side throttling/buffering was also revamped:
There was an issue doing the throttling inside the generator function,
it always waited for the timeout. The buffering is now removed from
`fetchStream`, instead `useThrottle` from `react-use` is used on the
reduced `data` in `useFetchStream`. Loading log rate analysis results
got a lot snappier with this update!
2023-07-27 08:57:10 +02:00

86 lines
3 KiB
TypeScript

/*
* 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 type { IRouter, Logger } from '@kbn/core/server';
import { streamFactory } from '@kbn/ml-response-stream/server';
import { simpleStringStreamRequestBodySchema } from '../../common/api/simple_string_stream';
import { RESPONSE_STREAM_API_ENDPOINT } from '../../common/api';
function timeout(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export const defineSimpleStringStreamRoute = (router: IRouter, logger: Logger) => {
router.versioned
.post({
path: RESPONSE_STREAM_API_ENDPOINT.SIMPLE_STRING_STREAM,
access: 'internal',
})
.addVersion(
{
version: '1',
validate: {
request: {
body: simpleStringStreamRequestBodySchema,
},
},
},
async (context, request, response) => {
const maxTimeoutMs = request.body.timeout ?? 250;
let shouldStop = false;
request.events.aborted$.subscribe(() => {
shouldStop = true;
});
request.events.completed$.subscribe(() => {
shouldStop = true;
});
const { end, push, responseWithHeaders } = streamFactory(
request.headers,
logger,
request.body.compressResponse
);
const text =
'Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents. Elasticsearch is developed in Java and is dual-licensed under the source-available Server Side Public License and the Elastic license, while other parts fall under the proprietary (source-available) Elastic License. Official clients are available in Java, .NET (C#), PHP, Python, Apache Groovy, Ruby and many other languages. According to the DB-Engines ranking, Elasticsearch is the most popular enterprise search engine.';
const tokens = text.split(' ');
async function pushStreamUpdate() {
try {
if (shouldStop) {
end();
return;
}
const token = tokens.shift();
if (token !== undefined) {
push(`${token} `);
await timeout(Math.floor(Math.random() * maxTimeoutMs));
if (!shouldStop) {
pushStreamUpdate();
}
} else {
end();
}
} catch (e) {
logger.error(`There was an error: ${e.toString()}`);
}
}
// do not call this using `await` so it will run asynchronously while we return the stream already.
pushStreamUpdate();
return response.ok(responseWithHeaders);
}
);
};