dart compute Illegal argument in isolate message - dart

I am using compute to do some work while keeping the UI running. The compute was working until I added another http call before it.
The working code is as follow
final ListRequest request =
ListRequest(baseUrl: env['SERVER_URL']!, path: '/Items');
_mainController.updateListItems(
await compute(_service.getItems, request));
I read some articles saying the function compute calls should be a top level function or a static function. However, the getItems is an instance function and there was no exception.
Recently I added a few lines and the code became
final Filter? filter = await _service.getFilter();
final ListRequest request =
ListRequest(baseUrl: env['SERVER_URL']!, path: '/Items');
request.filter = filter;
_mainController.updateListItems(
await compute(_service.getItems, request));
getFilter is a http call to retrieve some filter parameters from the backend.
Then I got the following error
Invalid argument(s): Illegal argument in isolate message: (object extends NativeWrapper - Library:'dart:io' Class: _SecureFilterImpl#13069316)
My dart and flutter versions are
Dart SDK version: 2.15.1 (stable)
Flutter 2.8.1
Thank you
=========================================================
Update
The Filter is
Filter {
String? itemLocationSuburb;
String? itemLocationPostcode;
}

Your _service service presumably contains a HttpClient. When you make a request through this client, it opens a connection to the HTTP server, and may maintain the connection after the request completes.
The HttpClient cannot be sent through a SendPort when it has open connections, but it is included in the scope of the getItems method.
To work around this issue, you can do one of the following:
Disable persistent connections with the HttpClientRequest.persistentConnection property
Make a new HttpClient to send through the compute function every time
Implement a long-lived background isolate to maintain its own HttpClient
Use the HttpClient in the main isolate, and only perform other work like parsing with compute (there's no significant benefit to using an isolate to make HTTP requests anyway)

Related

Blazor-server scoped services, closed connections, garbage collection

If I have a scoped service:
services.AddSingleton<MyScopedService>();
And in that service, an HTTP request is made:
HttpClient client = _clientFactory.CreateClient();
StringContent formData = ...;
HttpResponseMessage response = await client.PostAsync(uri, formData);
string data = await response.Content.ReadAsStringAsync();
I read here that for an AddScoped service, the service scope is the SignalR connection.
If the user closes the browser tab before the response is returned, the MyScopedService code still completes.
Could someone explain what happens to that MyScopedService instance? When is it considered out of scope? After the code completes? Is the time until it's garbage collected predictable?
I have a Blazor-server project using scoped dependency injections (fluxor, and a CircuitHandler), and I'm noticing that the total app memory increases with each new connection (obviously), but takes a while (minutes) for the memory to come down after the browser tabs are closed.
Just wondering if this is expected, or if I could be doing something to let the memory usage recover more quickly. Or maybe I'm doing something wrong with my scoped services.
Add IDisposeAsync to your service then in your service :
public async ValueTask DisposeAsync() => await hubConnection.DisposeAsync();
This was copied from one of my own libraries I was facing the same issue. GC will not work if there are references to other objects...

How to get Redis key values on Server side Dart with Angel

Having moved my mobile app development to Flutter I am now in the process of experimenting with using Dart as my main server side language. The productivity benefits in using a single coding language in both the app and on the server are considerable. To that end I have set up a server with an Nginx front end which proxies all dynamic web requests to an Angel/Dart server.
Angel is a remarkably well written package and I had a working server written up in no time at all. However, in order to have a fully functional backend I need to be able to use both Redis and PostgreSQL from within my server side Dart code. I am using the resp_client package to access Redis. The issue I have run into is with the fact that RespCommand.get is asynchronous. With my newbie knowledge of both Dart and Angel I am unable to find a way to acquire a Redis key value via RespCommand.get in an Angel route handler and then somehow use that value in the response it returns.
My entire Dart backend server code is shown below
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:postgres/postgres.dart';
import 'package:resp_client/resp_client.dart';
import 'package:resp_client/resp_commands.dart';
class DartWeb
{
static Angel angel;
static AngelHttp http;
static RespCommands redis;
static PostgreSQLConnection db;
static init() async
{
angel = Angel();
http = AngelHttp(angel);
angel.get('/',rootRoute);
await prepareRedis();
await http.startServer('localhost',3000);
}
static prepareRedis() async
{
RespServerConnection rsc = await connectSocket('localhost');
RespClient client = RespClient(rsc);
redis = RespCommands(client);
}
static preparePostgres() async
{
db = new PostgreSQLConnection('serverurl',portNo,'database',username:'user',password:'password');
await db.open();
}
static void rootRoute(RequestContext req,ResponseContext res)
{
try
{
await redis.set('test','foobar',expire:Duration(seconds:10));
String testVal = await redis.get('test');
res.write('Done $testVal');
} catch(e) {res.write('++ $e ++');}
}
}
main() async {await DartWeb.init();}
If I start up this server and then access it through my web browser I end up with a 502 Bad Gateway message. Not surprising. dart2native main.dart -o mainCompiled returns the error await can only be used in async... message.
So I tried instead
try
{
res.write('Before');
redis.set('test','foobar',expire:Duration(seconds:10)).then((bool done)
{
res.write('DONE $done');
});
res.write('After');
} catch(e) {res.write('++ $e ++');}
which simply printed out BeforeAfter in my browser with the DONE bit never showing up although a quick test via redis-cli shows that the key test had in fact been created.
My knowledge of both Dart and Angel is still in its infancy so I guess I am doing something incorrectly here. Shorn of all the detail my questions are essentially these -
how do I call and get the result from async methods in an Angel route dispatcher?
given that I am editing my Dart code in VSCode on my local Windows machine which accesses the relevant dart files on my Ubuntu server I loose the benefits of error reporting provided by the VSCode Dart plugin. dart2native, as I have used here, helps out but it would be nicer if I could somehow get a running error report within VSCode as I do when building Flutter apps locally. How can I accomplish this - if at all possible?
It turns out that Dart/Angel does not impose excessively strict constraints on the signature of a route handler. So you can quite safely declare a route handler like this one
static Future<void> rootRoute(RequestContext req,ResponseContext res) async
{
try
{
res.write('!! Before ');
await redis.set('test','foobar',expire:Duration(seconds:10));
String test = await redis.get('test');
res.write('After $test !!');
} catch(e) {res.write('++ $e ++');}
}
With the route simply returning a Future we can now safely do anything we like there - including calling other asynchronous methods: in this instance to fetch a Redis key value.

efficient async function that needs result from another async function in dart (http client)

From here Dart - Request GET with cookie we have this example of doing a get request with dart's built in HTTP library:
exampleCall() {
HttpClient client = new HttpClient();
HttpClientRequest clientRequest =
await client.getUrl(Uri.parse("http: //www.example.com/"));
clientRequest.cookies.add(Cookie("sessionid", "asdasdasqqwd"));
HttpClientResponse clientResponse = await clientRequest.close();
}
As you can see, multiple awaits are needed. Which means that if I try to do multiple concurrent exampleCall calls, they won't happen at the same time.
I cannot return a future because I must wait the client.getUrl() in order to do the clientResponse.
I also couldn't find a good alternative to use cookies on http calls. Dio seems to only support storing cookies from the server. Anyways, I'd like to know how to do in this way, but if there's a better way I'd like to know.
As you can see, multiple awaits are needed. Which means that if I try to do multiple concurrent exampleCall calls, they won't happen at the same time.
Not really sure what you mean here. Dart is single threaded so the concept of things happen "at the same time" is a little vauge. But if you follow the example later you should be able to call exampleCall() multiple times without the need of waiting on each other.
I cannot return a future because I must wait the client.getUrl() in order to do the clientResponse.
Yes you can if you mark the method as async:
import 'dart:convert';
import 'dart:io';
Future<List<String>> exampleCall() async {
final client = HttpClient();
final clientRequest =
await client.getUrl(Uri.parse("http://www.example.com/"));
clientRequest.cookies.add(Cookie("sessionid", "asdasdasqqwd"));
final clientResponse = await clientRequest.close();
return clientResponse
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
}
The whole point of async methods is the ability to easily bundle multiple asynchronous calls into a single Future. Notice, that async methods must always return a Future but your return statement should not necessarily return a Future object (if you return a normal object, it will automatically be packed into a Future).
I also couldn't find a good alternative to use cookies on http calls. Dio seems to only support storing cookies from the server. Anyways, I'd like to know how to do in this way, but if there's a better way I'd like to know.
Not really sure about the whole cookie situation. :)

Slow swagger Scanning - s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references

I am trying to add a grpc protofile to my swagger-ui. I am consuming a grpc webservice which needs a protofile as input. The input to my spring boot restful webservice needs to have that same grpc structure as its interface. I recevied a jar from the individual that made the protofile and imported it to my webserivce. When I try to add the #ResponseBody tag around the object from the protofile jar, my app hangs on this in the console at startup:
s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references
Thanks,
Brian
Never return entity objects in controller method.
in my case. my Controller methods takes this parameter.
"#AuthenticationPrincipal UserSession userSession"
when i exlude UserSession object swagger back to normal.
There were 2 way to do that
first is "#ApiIgnore #AuthenticationPrincipal UserSession userSession"
second is in swaggerConfig class
private Class[] clazz = {UserSession.class};
Docket().ignoredParameterTypes(clazz)
Incase someone needs a solution, what I did was as a work around for now.
in my service's code (response is a String)
return JsonFormat.printer().print(myProtoObject);
in my client's code:
Builder b = ProtoObject.newBuilder();
JsonFormat.parser().merge(result.getBody(), b);
ProtoObject protoObject = b.build();

SignalR in MVC3, timing and start/connect issues?

I am having a really weird issue with MVC3 and signalr.. I have a simple hub;
[HubName("test")]
public class Test: Hub
{
public object GetStuff()
{
return new { dummy = "Test" };
}
}
And some client-side code;
var connection = $.connection.test;
connection.start();
connection.getStuff();
This throws an error;
TypeError: Object # has no method 'start'
If I instead do
var connection = $.connection("test");
I get a different error;
TypeError: Object # has no method 'getStuff' jquery-1.6.4.min.js:4
POST http://localhost:63021/Controller/test/negotiate 405 (Method Not Allowed)
Note its trying to negotiate under the controller for some reason?
Is there some specific route I need to register? Some other magic I dont know about?
UPDATE
So playing a bit with console -- the first version does in fact create an object that has getStuff() which i can call. But signalr throws up because i have to call start() first -- which doesn't exist! The second one creates an object that DOES have start(), but it doesnt have getStuff()..
UPDATE 2
Tried doing $.connection.hub.start instead. This seems to work in the console, but not in the page onload.. Possibly start isnt finished before the hub call is made? Is it async?
Starting the SignalR connection is not instantaneous. You call to connection.GetStuff(); may fail if the connection has not yet been established. If you want this code to run after a connection to the hub is established you should use a callback function.
var connection = $.connection.test;
$.connection.hub.start(function(){
// By convention all exposed hub methods start with lowercase
connection.getStuff();
});
Hub Quickstart: https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs
In-depth look at SignalR javascript client: https://github.com/SignalR/SignalR/wiki/SignalR-JS-Client-Hubs
You must add the hub portion:
$.connection.hub.start();
Try this:
var connection = $.connection("#Url.Content("~/echo")");

Resources