Steadfastness of a remote answer collector, Web Prolog style

Sure, my first (crappy) implementation used posix threads (thread_create/3 and friends) only (just like library(pengines)), the current PoC uses Paul Tarau’s engines and fewer threads. An implementation in Erlang might use Erlang processes. An implementation in Java might use something else. What? Well, I leave that to you to propose. The point is that there are many way to implement Web Prolog, and I’m not yet sure which way is the best. I’m still in the specification phase, and would prefer to remain there for a while.

I’m not sure where you think setup_call_cleanup/3 would help. Can you elaborate?

exit(true) tells the pengine to die after having run the query to completion, wheras exit(false) means you can keep using it for more querying.

That could and probably should be added, but I still don’t understand how setup_call_cleanup/3 would help, so I would use pengine_exit/2 in combination with a timeout as in the previous example.

If you know the first solution is all you want, I would propose to use once(q(X)) as a goal. :wink:

But hey, I’m aware of the problem, and I’m not sure a 100% satisfying solutions exists for immediately killing (remote) pengines when they are no longer needed. I have a few ideas, and one of the best might be the following. A Web Prolog node can (and should) be equipped with a stateless (think RESTful) HTTP API as well as a WebSocket API. rpc/2-3, as implemented above, has to use the WebSocket API, but rpc/2-3 can also be implemented using HTTP as transport (one might use an option transport to choose between the two implementations). Here’s a somewhat ugly implementation:

rpc(URI, Query, Options) :-
    option(limit(Limit), Options, 1),
    rpc(URI, Query, 0, Limit).
    
rpc(URI, Query, Offset, Limit) :-
    parse_url(URI, Parts),
    format(atom(QueryAtom), "(~p)", [Query]),
    rpc(Query, Offset, Limit, QueryAtom, Parts).
    
rpc(Query, Offset, Limit, QueryAtom, Parts) :-    
    parse_url(ExpandedURI, [ path('/ask'),
                             search([ query=QueryAtom,
                                      offset=Offset,
                                      limit=Limit,
                                      format=prolog
                                    ])
                           | Parts]), 
    setup_call_cleanup(
        http_open(ExpandedURI, Stream, []),
        read(Stream, Answer), 
        close(Stream)),
    wait_answer(Answer, Query, Offset, Limit, QueryAtom, Parts).

wait_answer(error(anonymous, Error), _, _, _, _, _) :-
    throw(Error).
wait_answer(failure(anonymous), _, _, _, _, _) :-
    fail.
wait_answer(success(anonymous, Solutions, false), Query, _, _, _, _) :- 
    !,
    member(Query, Solutions).
wait_answer(success(anonymous, Solutions, true), 
            Query, Offset0, Limit, QueryAtom, Parts) :-
    (   member(Query, Solutions)
    ;   Offset is Offset0 + Limit,
        rpc(Query, Offset, Limit, QueryAtom, Parts)
    ).

To understand how the stateless HTTP is supposed to work, you should first look at section 4.9 in the paper, and if you want to know (much!) more, at section 6.3 - 6.5 in the longer manuscript. The tutorial in the PoC provides a couple of examples.