Open insinfo opened 7 months ago
I have the same problem. :\
I created a minimal app to reproduce the problem
I managed to reproduce the problem on my Windows 11 machine and running the app on WSL Debian 12, for the problem to occur I had to run wrk about 7 times ./wrk -t24 -c800 -d90s http://172.30.82.2: 3350
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_production/angel3_production.dart';
import 'package:angel3_cors/angel3_cors.dart';
import 'dart:convert';
import 'package:file/file.dart' as file;
import 'package:angel3_static/angel3_static.dart' as static_file_server;
import 'dart:async';
import 'package:file/local.dart';
//to run: dart ./bin/main.dart -p 3350 -a 0.0.0.0 -j 4
main(List<String> args) async {
return Runner('angel', configureServer).run(args);
}
Future configureServer(Angel app) async {
final options = CorsOptions();
app.fallback(cors(options));
final fs = const LocalFileSystem();
await app.configure(configureRoutes(fs));
}
AngelConfigurer configureRoutes(file.FileSystem fileSystem) {
return (Angel app) {
app.get('/', (req, res) => res.write('angel'));
// app.chain([simulatedMiddleware]).group('/api/v1', (router) {
// for (final sr in simulatedRoutes) {
// final method = sr.keys.first.toUpperCase();
// final path = sr.values.first;
// router.addRoute(method, path, (req, res) => simulatedWork);
// }
// });
for (final sr in simulatedRoutes) {
final method = sr.keys.first.toUpperCase();
final path = sr.values.first;
app.addRoute(method, '/api/v1' + path, (req, res) => simulatedWork,middleware: [simulatedMiddleware]);
}
if (!app.environment.isProduction) {
var vDir = static_file_server.VirtualDirectory(
app,
fileSystem,
source: fileSystem.directory('storage'),
);
app.fallback(vDir.handleRequest);
}
app.fallback((req, res) {
res.headers['Content-Type'] = 'application/json;charset=utf-8';
res.statusCode = 404;
return res.write(jsonEncode({
'message': 'Rota não existe',
'exception': 'Rota não existe',
'stackTrace': ''
}));
});
final oldErrorHandler = app.errorHandler;
app.errorHandler = (e, req, res) async {
if (req.accepts('text/html', strict: true)) {
if (e.statusCode == 404 && req.accepts('text/html', strict: true)) {
await res
.render('error', {'message': 'Arquivo não existe ${req.uri}.'});
} else {
await res.render('error', {'message': e.message});
}
} else {
return await oldErrorHandler(e, req, res);
}
};
};
}
final simulatedRoutes = [
{'get': '/administracao/paises'},
{'get': '/administracao/ufs'},
{'get': '/administracao/municipios'},
{'get': '/administracao/modulos'},
{'get': '/administracao/permissoes/:numCgm/:anoExercicio'},
{'put': '/administracao/permissoes/:numCgm/:anoExercicio'},
{'get': '/administracao/escolaridades'},
{'get': '/administracao/tiposlogradouro'},
{'get': '/administracao/orgaos'},
{'get': '/administracao/unidades'},
{'get': '/administracao/departamentos'},
{'get': '/administracao/setores'},
{'get': '/administracao/gestao'},
{'get': '/administracao/usuarios'},
{'get': '/administracao/usuarios/:numcgm'},
{'post': '/administracao/usuarios'},
{'put': '/administracao/usuarios'},
{'get': '/administracao/cgm'},
{'get': '/administracao/cgm/:cgm'},
{'get': '/administracao/auditorias'},
{'post': '/administracao/auditorias'},
{'get': '/administracao/configuracao'},
{'get': '/administracao/configuracao/by/filtro'},
{'get': '/administracao/funcionalidades'},
{'get': '/administracao/menu/:cgm'},
{'get': '/administracao/organograma/hierarquia'},
{'get': '/administracao/acoes'},
//auth
{'post': '/change/pass'},
{'get': '/auth/check/permissao/:cgm'},
{'post': '/auth/login'},
{'post': '/auth/check'},
{'get': '/auth/check/toke'},
//cgm
{'get': '/cgm/full'},
{'get': '/cgm/full/:cgm'},
{'delete': '/cgm'},
{'post': '/cgm/full'},
{'post': '/cgm/full/interno'},
{'put': '/cgm/full'},
{'get': '/cgm/atributos'},
{'get': '/cgm/atributos/:cgm'},
{'get': '/cgm/categoriashabilitacao'},
{'get': '/cgm/tiposlogradouro'},
//
{'get': '/estatistica/processos/ano'},
{'get': '/estatistica/processos/periodo/setor/primero/tramite'},
{'get': '/estatistica/processos/situacao'},
{'get': '/estatistica/processos/classificacao'},
{'get': '/estatistica/processos/assunto'},
//
{'get': '/norma/normas'},
//
{'get': '/protocolo/processos/favoritos/cgm/:cgm'},
{'post': '/protocolo/processos/favoritos'},
{'post': '/protocolo/processos/favoritos/:codProcesso/:anoExercicio'},
{'put': '/protocolo/processos/favoritos'},
{'delete': '/protocolo/processos/favoritos/:id'},
{'delete': '/protocolo/processos/favoritos/:codProcesso/:anoExercicio'},
{'get': '/protocolo/acoes/favoritas/cgm/:cgm'},
{'post': '/protocolo/acoes/favoritas'},
{'post': '/protocolo/acoes/favoritas/:codAcao'},
{'put': '/protocolo/acoes/favoritas'},
{'delete': '/protocolo/acoes/favoritas/:id'},
{'delete': '/protocolo/acoes/favoritas/:codAcao'},
{'get': '/protocolo/assuntos'},
{'post': '/protocolo/assuntos'},
{'put': '/protocolo/assuntos'},
{'delete': '/protocolo/assuntos'},
{'get': '/protocolo/assuntos/:codAssunto/:codClassificacao'},
{'get': '/protocolo/classificacoes'},
{'post': '/protocolo/classificacoes'},
{'put': '/protocolo/classificacoes'},
{'delete': '/protocolo/classificacoes'},
{'get': '/protocolo/despachospadrao'},
{'post': '/protocolo/despachospadrao'},
{'put': '/protocolo/despachospadrao'},
{'delete': '/protocolo/despachospadrao'},
{'get': '/protocolo/tramites'},
{'post': '/protocolo/tramites'},
{
'put':
'/protocolo/tramites/:codClassiOld/:codAssuntoOld/:ordemOld/:exercicioOld'
},
{'delete': '/protocolo/tramites'},
{'get': '/protocolo/processos/:anoExercicio/:codProcesso'},
{'get': '/protocolo/processos/em/apenso/:anoExercicio/:codProcesso'},
{'get': '/protocolo/processos/apenso/a/:anoExercicio/:codProcesso'},
{'get': '/protocolo/processos/andamentos/:anoExercicio/:codProcesso'},
{
'get':
'/protocolo/processos/despachos/:anoExercicio/:codProcesso/:codAndamento/:codUsuario/:timestamp'
},
{'get': '/protocolo/processos'},
{'get': '/protocolo/processos/areceber'},
{'get': '/protocolo/processos/aemcaminhar'},
{'get': '/protocolo/processos/byfiltros'},
{'get': '/protocolo/processos/aapensara'},
{'get': '/protocolo/processos/adesapensar'},
{'get': '/protocolo/processos/adespachar'},
{'get': '/protocolo/processos/aalterar'},
{'get': '/protocolo/processos/acancelar'},
{'get': '/protocolo/processos/aarquivar'},
{'get': '/protocolo/processos/adesarquivar'},
{'post': '/protocolo/processos'},
{'post': '/protocolo/processos/implantar'},
{'put': '/protocolo/processos'},
{'post': '/protocolo/processos/receber/lote'},
{'post': '/protocolo/processos/despachar/lote'},
{'post': '/protocolo/processos/encaminhar/lote'},
{'post': '/protocolo/processos/cancelarencaminhamento/lote'},
{'post': '/protocolo/processos/apensar/lote'},
{'post': '/protocolo/processos/desapensar/lote'},
{'post': '/protocolo/processos/arquivar/lote'},
{'post': '/protocolo/processos/desarquivar/lote'},
{'post': '/protocolo/processos/anexos'},
{'get': '/protocolo/processos/anexos/:codProcesso/:anoExercicio'},
{'get': '/protocolo/situacoes'},
{'get': '/protocolo/historicoarquivamento'},
{'post': '/protocolo/historicoarquivamento'},
{'put': '/protocolo/historicoarquivamento'},
{'delete': '/protocolo/historicoarquivamento'},
{'get': '/protocolo/listagemprocessos'},
{'post': '/protocolo/listagemprocessos'},
{'get': '/protocolo/documentos'},
{'post': '/protocolo/documentos'},
{'put': '/protocolo/documentos'},
{'delete': '/protocolo/documentos'},
{'get': '/protocolo/atributosprotocolo'},
{'post': '/protocolo/atributosprotocolo'},
{'put': '/protocolo/atributosprotocolo'},
{'delete': '/protocolo/atributosprotocolo'},
{'get': '/protocolo/processos/public/site/:anoExercicio/:codProcesso'},
];
Future<dynamic> simulatedWork(RequestContext req, ResponseContext res) async {
try {
// ignore: unused_local_variable
final filters = req.queryParameters;
final listItems = List.generate(150, (int index) => {'name': 'Jon_$index'});
res.statusCode = 200;
res.headers['Content-Type'] = 'application/json;charset=utf-8';
res.headers['total-records'] = '150';
res.write(jsonEncode(listItems));
} catch (e, s) {
print('simulatedWork@all $e $s');
var v = jsonEncode({
'is_error': true,
'status_code': 400,
'message': 'Error',
'exception': e.toString(),
'stackTrace': s.toString()
});
res.statusCode = 400;
res.headers['Content-Type'] = 'application/json;charset=utf-8';
res.write(v);
}
}
Future<bool> simulatedMiddleware(
RequestContext req, ResponseContext res) async {
try {
//print('call simulatedMiddleware');
return true;
} catch (e, s) {
print('simulatedMiddleware: $s $s');
throw AngelHttpException.notAuthenticated(message: '$e $s');
}
}
isaque@pmro174330:/mnt/c/MyDartProjects/dart_segment_fault$ dart ./bin/main.dart -p 3350 -a 0.0.0.0 -j 4
_ _ _ ____ _____ _ _____
/ \ | \ | |/ ___| ____| | |___ /
/ _ \ | \| | | _| _| | | |_ \
/ ___ \| |\ | |_| | |___| |___ ___) |
/_/ \_\_| \_|\____|_____|_____|____/
A batteries-included, full-featured, full-stack framework in Dart.
https://angel3-framework.web.app
Starting `angel` application...
2023-11-08 17:59:07 INFO [angel]: No `reflector` was passed to the Angel constructor, so reflection will not be available.
Features like controllers, constructor dependency injection, and `ioc` require reflection, and will not work without it.
For more, see the documentation:
https://docs.angel-dart.dev/guides/dependency-injection#enabling-dart-mirrors-or-other-reflection
2023-11-08 17:59:07 INFO [angel]: No `reflector` was passed to the Angel constructor, so reflection will not be available.
Features like controllers, constructor dependency injection, and `ioc` require reflection, and will not work without it.
For more, see the documentation:
https://docs.angel-dart.dev/guides/dependency-injection#enabling-dart-mirrors-or-other-reflection
2023-11-08 17:59:07 INFO [angel]: No `reflector` was passed to the Angel constructor, so reflection will not be available.
Features like controllers, constructor dependency injection, and `ioc` require reflection, and will not work without it.
For more, see the documentation:
https://docs.angel-dart.dev/guides/dependency-injection#enabling-dart-mirrors-or-other-reflection
2023-11-08 17:59:07 INFO [angel]: No `reflector` was passed to the Angel constructor, so reflection will not be available.
Features like controllers, constructor dependency injection, and `ioc` require reflection, and will not work without it.
For more, see the documentation:
https://docs.angel-dart.dev/guides/dependency-injection#enabling-dart-mirrors-or-other-reflection
2023-11-08 17:59:08 INFO [angel]: Instance #1 listening at http://0.0.0.0:3350
2023-11-08 17:59:08 INFO [angel]: Instance #3 listening at http://0.0.0.0:3350
2023-11-08 17:59:08 INFO [angel]: Instance #2 listening at http://0.0.0.0:3350
2023-11-08 17:59:08 INFO [angel]: Instance #0 listening at http://0.0.0.0:3350
../../runtime/vm/raw_object.cc: 356: error: Invalid cid: 0, obj: 0x7fc3e24c2948, tags: 20. Corrupt heap?
version=2.18.7 (stable) (Unknown timestamp) on "linux_x64"
pid=155, thread=174, isolate_group=main(0x55798c1ab800), isolate=(nil)((nil))
isolate_instructions=557989298680, vm_instructions=557989298680
pc 0x00005579894fd67c fp 0x00007fc3d937a8f0 dart::Profiler::DumpStackTrace(void*)+0x7c
pc 0x0000557989298834 fp 0x00007fc3d937a9d0 dart::Assert::Fail(char const*, ...) const+0x84
pc 0x000055798950e17e fp 0x00007fc3d937aa00 dart::UntaggedObject::VisitPointersPredefined(dart::ObjectPointerVisitor*, long)+0x4de
pc 0x00005579895bb13b fp 0x00007fc3d937aa90 void dart::Scavenger::IterateStoreBuffers<true>(dart::ScavengerVisitorBase<true>*)+0xcb
pc 0x00005579895bb00b fp 0x00007fc3d937ab10 void dart::Scavenger::IterateRoots<true>(dart::ScavengerVisitorBase<true>*)+0x16b
pc 0x00005579895bad32 fp 0x00007fc3d937ac30 dart+0x2225d32
pc 0x00005579895b9bef fp 0x00007fc3d937acb0 dart+0x2224bef
pc 0x00005579895babce fp 0x00007fc3d937ace0 dart+0x2225bce
pc 0x00005579895867b8 fp 0x00007fc3d937ad60 dart::ThreadPool::WorkerLoop(dart::ThreadPool::Worker*)+0x148
pc 0x0000557989586c0d fp 0x00007fc3d937ad90 dart::ThreadPool::Worker::Main(unsigned long)+0x6d
pc 0x00005579894f9858 fp 0x00007fc3d937ae50 dart+0x2164858
-- End of DumpStackTrace
Aborted
This is not really Angel3 specific issue. Angel3 is run with -j 4
which means using only 4 isolates. On the other hand wrk is run with -t12 -c400
which means 12 threads with 400 connections on each thread. There is a limit to the load that 4 isolates can deal with before being overloaded. I would suggest to try -j 100
or more to handle this kind of load. Memory and number of CPU cores also play a part in how much concurrency can get out of it.
I did several tests here with the shelf and the problem didn't happen, and I saw that this problem only happens using the angel, and it seems to be linked to something in the router, most likely something related to the chain/group method perhaps, or the Runner
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:args/args.dart';
import 'package:prometheus_client/format.dart' as format;
import 'package:prometheus_client/prometheus_client.dart';
import 'package:prometheus_client/runtime_metrics.dart' as runtime_metrics;
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
import 'package:stack_trace/stack_trace.dart';
import 'package:dart_segment_fault/dependencies/shelf_cors_headers_base/shelf_cors_headers_base.dart';
import 'package:dart_segment_fault/dependencies/stream_isolate/stream_isolate.dart';
// to compile
// dart compile exe -o backend.exe .\bin\backend.dart
// to test
// xargs -I % -P 8 curl "http:/192.168.66.123:3161/api/v1/protocolo/processos/public/site/2023/10" < <(printf '%s\n' {1..400})
//./wrk -t12 -c400 -d30s http://172.30.82.2:3350
// bombardier.exe -c400 -d30s http://192.168.66.123:3350/metrics
final streamIsolates = <Map<int, BidirectionalStreamIsolate>>[];
void main(List<String> args) async {
final parser = ArgParser()
..addOption('address', abbr: 'a', defaultsTo: '0.0.0.0')
..addOption('port', abbr: 'p', defaultsTo: '3161')
..addOption('isolates', abbr: 'j', defaultsTo: '3');
final argsParsed = parser.parse(args);
final arguments = [argsParsed['address'], int.parse(argsParsed['port'])];
final numberOfIsolates = int.parse(argsParsed['isolates']);
for (var i = 0; i < numberOfIsolates - 1; i++) {
final streamIsolate = await StreamIsolate.spawnBidirectional(isolateMain,
debugName: i.toString(), argument: [i, ...arguments]);
streamIsolates.add({i: streamIsolate});
streamIsolate.stream.listen((event) => receiveAndPass(event, i));
}
}
/// receive msg from isolate and send to all isolates
void receiveAndPass(event, int idx) {
streamIsolates.forEach((item) {
item.values.first.send(event);
});
}
Stream isolateMain(Stream inc, dynamic args) {
final arguments = args as List;
int id = arguments[0];
String address = arguments[1];
int port = arguments[2];
final streamController = StreamController.broadcast();
final reg = CollectorRegistry();
// Register default runtime metrics
runtime_metrics.register(reg);
// Register http requests total
final http_requests_total = Counter(
name: 'http_requests_total', help: 'Total number of http api requests');
http_requests_total.register(reg);
// listen msg from main
inc.listen((msg) {
http_requests_total.inc();
});
_startServer([id, streamController, reg, address, port]);
return streamController.stream;
}
void _startServer(List args) async {
final streamController = args[1] as StreamController;
final reg = args[2] as CollectorRegistry;
String address = args[3];
int port = args[4];
final app = Router();
routes(app, reg);
final handler =
Pipeline().addMiddleware(corsHeaders()).addMiddleware((innerHandler) {
return (request) async {
// Every time http_request is called, increase the counter by one
final resp = await innerHandler(request);
if (request.url.path != 'metrics') {
//send msg to main
streamController.add('+1');
}
return resp;
};
})
//.addMiddleware(logRequestsCustom())
.addHandler(app);
final server = await io.serve(handler, address, port, shared: true);
server.defaultResponseHeaders.remove('X-Frame-Options', 'SAMEORIGIN');
print('Serving at http://${server.address.host}:${server.port}');
}
void routes(Router app, CollectorRegistry reg) {
app.get('/', (Request request) async {
return Response.ok('shelf');
});
// Register a handler to expose the metrics in the Prometheus text format
app.get('/metrics', (Request request) async {
final buffer = StringBuffer();
final metrics = await reg.collectMetricFamilySamples();
format.write004(buffer, metrics);
return Response.ok(
buffer.toString(),
headers: {'Content-Type': format.contentType},
);
});
app.group('/api/v1', (router) {
for (final sr in simulatedRoutes) {
final method = sr.keys.first.toUpperCase();
final path = sr.values.first;
router.add(method, path, simulatedWork);
}
},middleware: simulatedMiddleware);
}
class RouterContext {
final Router _router;
final String basePath;
final Middleware? middleware;
RouterContext(
this._router, {
required this.basePath,
this.middleware,
});
void add(
String verb, String route, FutureOr<Response> Function(Request) handler) {
if (middleware != null) {
_router.add(verb, basePath + route, middleware!(handler));
} else {
_router.add(verb, basePath + route, handler);
}
}
}
extension RouterExtension on Router {
void group(String basePath, Function(RouterContext) callback,
{Middleware? middleware}) {
final ctx = RouterContext(this, basePath: basePath);
callback(ctx);
}
}
FutureOr<Response> Function(Request) simulatedMiddleware(innerHandler) {
return (request) async {
final resp = await innerHandler(request);
return resp;
};
}
Future<Response> simulatedWork(Request request) async {
try {
final listItems = List.generate(150, (int index) => {'name': 'Jon_$index'});
return Response.ok(
jsonEncode(listItems),
headers: {'Content-Type': 'application/json;charset=utf-8'},
);
} catch (e, s) {
print('simulatedWork@all $e $s');
return Response.badRequest();
}
}
final simulatedRoutes = [
{'get': '/administracao/paises'},
{'get': '/administracao/ufs'},
{'get': '/administracao/municipios'},
{'get': '/administracao/modulos'},
{'get': '/administracao/permissoes/:numCgm/:anoExercicio'},
{'put': '/administracao/permissoes/:numCgm/:anoExercicio'},
{'get': '/administracao/escolaridades'},
{'get': '/administracao/tiposlogradouro'},
{'get': '/administracao/orgaos'},
{'get': '/administracao/unidades'},
{'get': '/administracao/departamentos'},
{'get': '/administracao/setores'},
{'get': '/administracao/gestao'},
{'get': '/administracao/usuarios'},
{'get': '/administracao/usuarios/:numcgm'},
{'post': '/administracao/usuarios'},
{'put': '/administracao/usuarios'},
{'get': '/administracao/cgm'},
{'get': '/administracao/cgm/:cgm'},
{'get': '/administracao/auditorias'},
{'post': '/administracao/auditorias'},
{'get': '/administracao/configuracao'},
{'get': '/administracao/configuracao/by/filtro'},
{'get': '/administracao/funcionalidades'},
{'get': '/administracao/menu/:cgm'},
{'get': '/administracao/organograma/hierarquia'},
{'get': '/administracao/acoes'},
//auth
{'post': '/change/pass'},
{'get': '/auth/check/permissao/:cgm'},
{'post': '/auth/login'},
{'post': '/auth/check'},
{'get': '/auth/check/toke'},
//cgm
{'get': '/cgm/full'},
{'get': '/cgm/full/:cgm'},
{'delete': '/cgm'},
{'post': '/cgm/full'},
{'post': '/cgm/full/interno'},
{'put': '/cgm/full'},
{'get': '/cgm/atributos'},
{'get': '/cgm/atributos/:cgm'},
{'get': '/cgm/categoriashabilitacao'},
{'get': '/cgm/tiposlogradouro'},
//
{'get': '/estatistica/processos/ano'},
{'get': '/estatistica/processos/periodo/setor/primero/tramite'},
{'get': '/estatistica/processos/situacao'},
{'get': '/estatistica/processos/classificacao'},
{'get': '/estatistica/processos/assunto'},
//
{'get': '/norma/normas'},
//
{'get': '/protocolo/processos/favoritos/cgm/:cgm'},
{'post': '/protocolo/processos/favoritos'},
{'post': '/protocolo/processos/favoritos/:codProcesso/:anoExercicio'},
{'put': '/protocolo/processos/favoritos'},
{'delete': '/protocolo/processos/favoritos/:id'},
{'delete': '/protocolo/processos/favoritos/:codProcesso/:anoExercicio'},
{'get': '/protocolo/acoes/favoritas/cgm/:cgm'},
{'post': '/protocolo/acoes/favoritas'},
{'post': '/protocolo/acoes/favoritas/:codAcao'},
{'put': '/protocolo/acoes/favoritas'},
{'delete': '/protocolo/acoes/favoritas/:id'},
{'delete': '/protocolo/acoes/favoritas/:codAcao'},
{'get': '/protocolo/assuntos'},
{'post': '/protocolo/assuntos'},
{'put': '/protocolo/assuntos'},
{'delete': '/protocolo/assuntos'},
{'get': '/protocolo/assuntos/:codAssunto/:codClassificacao'},
{'get': '/protocolo/classificacoes'},
{'post': '/protocolo/classificacoes'},
{'put': '/protocolo/classificacoes'},
{'delete': '/protocolo/classificacoes'},
{'get': '/protocolo/despachospadrao'},
{'post': '/protocolo/despachospadrao'},
{'put': '/protocolo/despachospadrao'},
{'delete': '/protocolo/despachospadrao'},
{'get': '/protocolo/tramites'},
{'post': '/protocolo/tramites'},
{
'put':
'/protocolo/tramites/:codClassiOld/:codAssuntoOld/:ordemOld/:exercicioOld'
},
{'delete': '/protocolo/tramites'},
{'get': '/protocolo/processos/:anoExercicio/:codProcesso'},
{'get': '/protocolo/processos/em/apenso/:anoExercicio/:codProcesso'},
{'get': '/protocolo/processos/apenso/a/:anoExercicio/:codProcesso'},
{'get': '/protocolo/processos/andamentos/:anoExercicio/:codProcesso'},
{
'get':
'/protocolo/processos/despachos/:anoExercicio/:codProcesso/:codAndamento/:codUsuario/:timestamp'
},
{'get': '/protocolo/processos'},
{'get': '/protocolo/processos/areceber'},
{'get': '/protocolo/processos/aemcaminhar'},
{'get': '/protocolo/processos/byfiltros'},
{'get': '/protocolo/processos/aapensara'},
{'get': '/protocolo/processos/adesapensar'},
{'get': '/protocolo/processos/adespachar'},
{'get': '/protocolo/processos/aalterar'},
{'get': '/protocolo/processos/acancelar'},
{'get': '/protocolo/processos/aarquivar'},
{'get': '/protocolo/processos/adesarquivar'},
{'post': '/protocolo/processos'},
{'post': '/protocolo/processos/implantar'},
{'put': '/protocolo/processos'},
{'post': '/protocolo/processos/receber/lote'},
{'post': '/protocolo/processos/despachar/lote'},
{'post': '/protocolo/processos/encaminhar/lote'},
{'post': '/protocolo/processos/cancelarencaminhamento/lote'},
{'post': '/protocolo/processos/apensar/lote'},
{'post': '/protocolo/processos/desapensar/lote'},
{'post': '/protocolo/processos/arquivar/lote'},
{'post': '/protocolo/processos/desarquivar/lote'},
{'post': '/protocolo/processos/anexos'},
{'get': '/protocolo/processos/anexos/:codProcesso/:anoExercicio'},
{'get': '/protocolo/situacoes'},
{'get': '/protocolo/historicoarquivamento'},
{'post': '/protocolo/historicoarquivamento'},
{'put': '/protocolo/historicoarquivamento'},
{'delete': '/protocolo/historicoarquivamento'},
{'get': '/protocolo/listagemprocessos'},
{'post': '/protocolo/listagemprocessos'},
{'get': '/protocolo/documentos'},
{'post': '/protocolo/documentos'},
{'put': '/protocolo/documentos'},
{'delete': '/protocolo/documentos'},
{'get': '/protocolo/atributosprotocolo'},
{'post': '/protocolo/atributosprotocolo'},
{'put': '/protocolo/atributosprotocolo'},
{'delete': '/protocolo/atributosprotocolo'},
{'get': '/protocolo/processos/public/site/:anoExercicio/:codProcesso'},
];
Middleware logRequestsCustom(
{void Function(String message, bool isError)? logger}) =>
(innerHandler) {
final theLogger = logger ?? _defaultLogger;
return (request) {
var startTime = DateTime.now();
var watch = Stopwatch()..start();
return Future.sync(() => innerHandler(request)).then((response) {
var msg = _message(startTime, response.statusCode,
request.requestedUri, request.method, watch.elapsed);
theLogger(msg, false);
return response;
}, onError: (Object error, StackTrace stackTrace) {
if (error is HijackException) throw error;
var msg = _errorMessage(startTime, request.requestedUri,
request.method, watch.elapsed, error, stackTrace);
theLogger(msg, true);
// ignore: only_throw_errors
throw error;
});
};
};
String _formatQuery(String query) {
return query == '' ? '' : '?$query';
}
String _message(DateTime requestTime, int statusCode, Uri requestedUri,
String method, Duration elapsedTime) {
return '${requestTime.toIso8601String()} '
'${elapsedTime.toString().padLeft(15)} '
'${method.padRight(7)} [$statusCode] ' // 7 - longest standard HTTP method
'${requestedUri.path}${_formatQuery(requestedUri.query)}'
' isolate: ${Isolate.current.debugName}';
}
String _errorMessage(DateTime requestTime, Uri requestedUri, String method,
Duration elapsedTime, Object error, StackTrace? stack) {
var chain = Chain.current();
if (stack != null) {
chain = Chain.forTrace(stack)
.foldFrames((frame) => frame.isCore || frame.package == 'shelf')
.terse;
}
var msg = '$requestTime\t$elapsedTime\t$method\t${requestedUri.path}'
'${_formatQuery(requestedUri.query)}\n$error';
return '$msg\n$chain';
}
void _defaultLogger(String msg, bool isError) {
if (isError) {
print('[ERROR] $msg');
} else {
print(msg);
}
}
I saw in the Runner code that Zone and pub_sub are being used, what are they used for?
@dukefirehawk Sorry for expressing myself poorly, English is not my native language, what I meant to say is more specifically in the context of Angel. Why does Angel need Zone and pubsub? Which angel feature depends on this? Because I removed the zone and pubsub from the Runner and the Angel ran apparently normally, In the context of Angel, the Zone is being used to isolate unhandled Exception, to prevent the application from terminating if an exception occurs? And what is pubsub for?
You are right. Zone is to prevent unexpected exception from shutting down the entire application. To the best of my knowledge pub_sub
is to facilitate communication between main isolate and spawned isolates.
I opened an issue in the dart SDK repository, pointing out this CRASH and @mraleph discovered the problem and it has already been fixed in dart 2.19. https://github.com/dart-lang/sdk/issues/53984
To the best of my knowledge pub_sub is to facilitate communication between main isolate and spawned isolates.
Do you know if Angel uses this somewhere or if it is for Angel's consumer user to use it? Could this be used to capture metrics to get the number of requests per second from all isolates?
Good to know that it is a resolved bug in the Dart SDK. angel3_production
is using it, see IsolateAdapter
. It is possible to be used for capturing metrics. Anyway I've started looking into performance tuning.
I'm getting this error in my application with Angel 7.0.0 and dart 2.18.7 on Debian 11 with libc 2.31
the error is intermittent, in the tests I did when I run wrk for the fifth time the error appears and angel restarts
/wrk -t12 -c400 -d30s http://localhost:3150
error in AOT mode
error in JIT mode
pubspec.yaml
wrk out