Interact + Mux: how to get independent connections?

Hello, I am trying to get a model whose controls and results are exposed on the web (for now on my own development machine).

I did manage to get a dummy model working, but I noticed that different connections are not independent, i.e. going to http://localhost:8001 with different browsers I receive starting values of the widgets set by the previous user(s), and the plot getting updated based on the changes made by the other user(s).
How can I get independent connections ?

This is the toy model I am working with:

using Interact,Plots,Mux

function myModel(p1,p2,xmin=-50,xmax=50)
    if xmax < xmin
        xmax = xmin
    end
    xrange = collect(xmin:xmax)
    model = p1.*xrange .+ p2.* (xrange) .^2
    return model
end


function createLayout()
  p1s = slider(-20:20, label = "Par#1 (linear term):", value = 1)
  p2s = slider(-5:5, label = "Par#2 (quad term):", value = 1)
  #xmins = slider(0:100, label = "Choose xmin:", value = 0)
  #xmaxs = slider(0:100, label = "Choose xmax:", value = 50)
  mOutput = Interact.@map myModel(&p1s,&p2s)
  plt = Interact.@map plot(collect(-50:50),&mOutput, label="Model output")
  wdg = Widget(["p1" => p1s, "p2" => p2s], output = mOutput)  
  @layout! wdg hbox(plt, vbox(:p1, :p2))
end
myLayout = createLayout()

WebIO.webio_serve(page("/", req -> myLayout), 8001)

I don’t know if it is related, I am receiving the following error when exposing the layout trough Mux, although it seems the error is recovered (timeout related?):

julia> β”Œ Error: error handling request
β”‚   exception =
β”‚    IOError: stream is closed or unusable
β”‚    Stacktrace:
β”‚     [1] check_open at ./stream.jl:323 [inlined]
β”‚     [2] uv_write_async(::Sockets.TCPSocket, ::Ptr{UInt8}, ::UInt64) at ./stream.jl:871
β”‚     [3] uv_write(::Sockets.TCPSocket, ::Ptr{UInt8}, ::UInt64) at ./stream.jl:845
β”‚     [4] unsafe_write(::Sockets.TCPSocket, ::Ptr{UInt8}, ::UInt64) at ./stream.jl:901
β”‚     [5] unsafe_write at /home/lobianco/.julia/packages/HTTP/U2ZVp/src/ConnectionPool.jl:134 [inlined]
β”‚     [6] macro expansion at ./gcutils.jl:87 [inlined]
β”‚     [7] write at ./strings/io.jl:165 [inlined]
β”‚     [8] closebody at /home/lobianco/.julia/packages/HTTP/U2ZVp/src/Streams.jl:111 [inlined]
β”‚     [9] closewrite(::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}) at /home/lobianco/.julia/packages/HTTP/U2ZVp/src/Streams.jl:126
β”‚     [10] (::getfield(HTTP.Servers, Symbol("##13#14")){getfield(WebSockets, Symbol("#_servercoroutine#11")){WebSockets.ServerWS},HTTP.ConnectionPool.Transaction{Sockets.TCPSocket},HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}})() at ./task.jl:259
β”” @ HTTP.Servers ~/.julia/packages/HTTP/U2ZVp/src/Servers.jl:364
Error handling websocket connection:
WebSockets.WebSocketClosedError("ws|server respond to OPCODE_CLOSE 1001:Going Away")
Stacktrace:
 [1] try_yieldto(::typeof(Base.ensure_rescheduled), ::Base.RefValue{Task}) at ./event.jl:196
 [2] wait() at ./event.jl:255
 [3] wait(::Condition) at ./event.jl:46
 [4] wait(::Task) at ./task.jl:188
 [5] create_socket(::Dict{Any,Any}) at /home/lobianco/.julia/packages/WebIO/9qsL8/src/providers/mux.jl:44
 [6] (::getfield(Mux, Symbol("##5#6")){getfield(Mux, Symbol("##28#29")){Array{SubString{String},1}},typeof(WebIO.create_socket)})(::Function, ::Dict{Any,Any}) at /home/lobianco/.julia/packages/Mux/FeATY/src/Mux.jl:17
 [7] #1 at /home/lobianco/.julia/packages/Mux/FeATY/src/Mux.jl:10 [inlined]
 [8] splitquery(::getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##5#6")){getfield(Mux, Symbol("##28#29")){Array{SubString{String},1}},typeof(WebIO.create_socket)},getfield(Mux, Symbol("##1#2")){typeof(Mux.wclose),getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##18#19")){getfield(Mux, Symbol("##22#23")){Symbol,Int64}},getfield(Mux, Symbol("##20#21")){String}}}}, ::Dict{Any,Any}) at /home/lobianco/.julia/packages/Mux/FeATY/src/basics.jl:34
 [9] #1 at /home/lobianco/.julia/packages/Mux/FeATY/src/Mux.jl:10 [inlined]
 [10] wcatch(::getfield(Mux, Symbol("##1#2")){typeof(Mux.splitquery),getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##5#6")){getfield(Mux, Symbol("##28#29")){Array{SubString{String},1}},typeof(WebIO.create_socket)},getfield(Mux, Symbol("##1#2")){typeof(Mux.wclose),getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##18#19")){getfield(Mux, Symbol("##22#23")){Symbol,Int64}},getfield(Mux, Symbol("##20#21")){String}}}}}, ::Dict{Any,Any}) at /home/lobianco/.julia/packages/Mux/FeATY/src/websockets_integration.jl:12
 [11] todict at /home/lobianco/.julia/packages/Mux/FeATY/src/Mux.jl:10 [inlined]
 [12] #3 at /home/lobianco/.julia/packages/Mux/FeATY/src/Mux.jl:14 [inlined] (repeats 2 times)
 [13] (::getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##3#4")){getfield(Mux, Symbol("##3#4")){typeof(Mux.todict),typeof(Mux.wcatch)},typeof(Mux.splitquery)},getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##5#6")){getfield(Mux, Symbol("##28#29")){Array{SubString{String},1}},typeof(WebIO.create_socket)},getfield(Mux, Symbol("##1#2")){typeof(Mux.wclose),getfield(Mux, Symbol("##1#2")){getfield(Mux, Symbol("##18#19")){getfield(Mux, Symbol("##22#23")){Symbol,Int64}},getfield(Mux, Symbol("##20#21")){String}}}}})(::Tuple{HTTP.Messages.Request,WebSockets.WebSocket{Sockets.TCPSocket}}) at /home/lobianco/.julia/packages/Mux/FeATY/src/Mux.jl:10
 [14] (::getfield(Mux, Symbol("##9#10")){Mux.App})(::HTTP.Messages.Request, ::WebSockets.WebSocket{Sockets.TCPSocket}) at /home/lobianco/.julia/packages/Mux/FeATY/src/server.jl:49
 [15] upgrade(::getfield(Mux, Symbol("##9#10")){Mux.App}, ::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}) at /home/lobianco/.julia/packages/WebSockets/pc4iW/src/HTTP.jl:201
 [16] (::getfield(WebSockets, Symbol("#_servercoroutine#11")){WebSockets.ServerWS})(::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}) at /home/lobianco/.julia/packages/WebSockets/pc4iW/src/HTTP.jl:370
 [17] macro expansion at /home/lobianco/.julia/packages/HTTP/U2ZVp/src/Servers.jl:360 [inlined]
 [18] (::getfield(HTTP.Servers, Symbol("##13#14")){getfield(WebSockets, Symbol("#_servercoroutine#11")){WebSockets.ServerWS},HTTP.ConnectionPool.Transaction{Sockets.TCPSocket},HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}})() at ./task.jl:259

I’m not sure about the timeout error, but to get independent apps you can simply do:

WebIO.webio_serve(page("/", req -> createLayout()), 8001)

rather than

WebIO.webio_serve(page("/", req -> myLayout), 8001)

so that each connection gets independent widgets.

1 Like

Thank you, got it.
Concerning the IOError, I think it is related to https://github.com/JuliaWeb/HTTP.jl/issues/392.

I solved the way it is suggested there:

function serveLayout(port)
    try
      WebIO.webio_serve(page("/", req -> createLayout()), port)
    catch e
      if isa(e, IOError)
        # sleep and then try again
        sleep(0.1)
        serveLayout(port)
      else
        throw(e)
      end
    end
end

serveLayout(8001)