client-server split for sbt.next

476 views
Skip to first unread message

Havoc Pennington

unread,
Oct 7, 2013, 5:15:55 PM10/7/13
to sbt...@googlegroups.com
Hi,

I created this wiki page about a proposed client-server mode for sbt.next:

https://github.com/sbt/sbt/wiki/Client-server-split

This is all preliminary, subject to change, and open to discussion. At
Mark's excellent suggestion, I'm posting before building consensus or
figuring it out - so we can hash it out here on the list.

Many of the details and proposed solutions I threw on the wiki may
turn out to be wrong, or collectively we might find better paths. In
fact I hope so. :-)

Questions, suggestions, "what abouts," "sounds goods," or whatever
else you have, please pile on.

Thanks,
Havoc

Josh Suereth

unread,
Oct 8, 2013, 7:16:33 PM10/8/13
to sbt...@googlegroups.com

Maybe we need to link proposed ideas to problems, to ensure we have solutions to everything.

I think the layout is great, but wanted to make sure we have viable solutions to each problem.

--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zCqYC2KnqkNrubKJ8eedYXa_fc20jb0ijgnMDhpAJy%3DzQ%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.

Havoc Pennington

unread,
Oct 9, 2013, 10:48:43 AM10/9/13
to sbt-dev
Hi,

On Tue, Oct 8, 2013 at 7:16 PM, Josh Suereth <joshua....@gmail.com> wrote:
> Maybe we need to link proposed ideas to problems, to ensure we have
> solutions to everything.
>
> I think the layout is great, but wanted to make sure we have viable
> solutions to each problem.
>

Sure - I was just trying to reduce clutter, the "solution specs" can
be much longer than the problem statements.

I added an example link to a solution discussion for the first problem
(startup/discovery/lifecycle). (That link is to
https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle )

We could create similar pages as needed maybe?

fwiw I'm confident these are all solvable problems - famous last words
:-) Though as always it's easy to miss an important consideration (or
to overcomplicate for a consideration that turns out to be not
important), so a couple iterations may be needed. Talking it through
as a group should help with some of those mistakes.

Finding the minimal place to start and then elaborating might help.
That is, get quickly to working code (a minimal client-server setup),
and then proceed to solve issues such as the user-interaction API,
command line completion, backgrounding tasks, etc. etc. all while
keeping the code working... we know we can do at least as well as
current sbt-remote-control without sbt changes, so we could get there
and then start fixing problems perhaps? That way all the hardest
problems are postponed until there's working code to tackle them on
top of.

Creating a "debug" JS UI that could be served up by the sbt server
itself might be great for testing ? If we have a little JS visualizer
of the work queue, then we could load up multiple instances of it and
test new functionality with that... basically a test client that would
always be available when we run the server...

For starting with a minimal client-server setup, the obvious initial
steps would be questions like "how do we locate and start the server?"
and "what's the protocol?" - so I started with "how do we locate and
start the server?"

Maybe a natural next topic would be "how do we support degraded 0.13
mode?" (e.g. through your xsbti.Server launcher extension idea or
what), and just generally where the code lives (what jars/processes).

I guess there's a chunk of code which is the "server shell" containing
server single-instancing/startup and listening on a port and
marshaling/demarshaling protocol, and this chunk of code would ideally
be independent of sbt itself similar to how the launcher is - a sort
of "sbt container" - maybe that's the first chunk of code? Want to
write down the xsbti.Server idea?

Network protocol seems like the next key question on the path to a
minimal setup...

Havoc

Mark Harrah

unread,
Oct 9, 2013, 11:11:15 AM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 10:48:43 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> Hi,
>
> On Tue, Oct 8, 2013 at 7:16 PM, Josh Suereth <joshua....@gmail.com> wrote:
> > Maybe we need to link proposed ideas to problems, to ensure we have
> > solutions to everything.
> >
> > I think the layout is great, but wanted to make sure we have viable
> > solutions to each problem.
> >
>
> Sure - I was just trying to reduce clutter, the "solution specs" can
> be much longer than the problem statements.
>
> I added an example link to a solution discussion for the first problem
> (startup/discovery/lifecycle). (That link is to
> https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle )
>
> We could create similar pages as needed maybe?

Yes, I like this approach. It is indeed nice to see all of the problems grouped quickly and I agree the solutions will be long and would clutter it.

-Mark

Mark Harrah

unread,
Oct 9, 2013, 11:16:15 AM10/9/13
to sbt...@googlegroups.com
One more process thing for docs: how should we deal with changes? I like that it is on the wiki because it is tied to the project, but it doesn't do comments/replies like Google Docs. Should we have an owner for each document and do comments/discussions here and the owner is responsible for accepting changes and updating? Have a separate project and open issues for each discussion point? Something else?

-Mark

Mark Harrah

unread,
Oct 9, 2013, 11:51:01 AM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 10:48:43 -0400
Havoc Pennington <h...@typesafe.com> wrote:

[...]

> Finding the minimal place to start and then elaborating might help.
> That is, get quickly to working code (a minimal client-server setup),
> and then proceed to solve issues such as the user-interaction API,
> command line completion, backgrounding tasks, etc. etc. all while
> keeping the code working... we know we can do at least as well as
> current sbt-remote-control without sbt changes, so we could get there
> and then start fixing problems perhaps? That way all the hardest
> problems are postponed until there's working code to tackle them on
> top of.

I know Josh likes to put the hard problems up front to fail fast. I'm ok with both of these (although they do conflict a bit), but one important thing is to ensure no one gets blocked, since there are several of us.

So...

> Creating a "debug" JS UI that could be served up by the sbt server
> itself might be great for testing ? If we have a little JS visualizer
> of the work queue, then we could load up multiple instances of it and
> test new functionality with that... basically a test client that would
> always be available when we run the server...
>
> For starting with a minimal client-server setup, the obvious initial
> steps would be questions like "how do we locate and start the server?"
> and "what's the protocol?" - so I started with "how do we locate and
> start the server?"

I agree that dealing with this is a good first implementation step. A visualizer sounds good too.

To avoid blocking, I think we'll need to identify a general roadmap with dependencies between tasks and priorities. I'll take a shot at this.

> Maybe a natural next topic would be "how do we support degraded 0.13
> mode?" (e.g. through your xsbti.Server launcher extension idea or
> what), and just generally where the code lives (what jars/processes).

I was thinking we should defer this, at least if you mean supporting 0.13 in general and not specifically the client-server setup. We'd make the changes to sbt as necessary and work with that. This somewhat assumes these can be done in a compatible way so that we can still test old projects.

If you mean the client-server setup, I agree with Josh that it could be done via the launcher in a way that you might only need minor changes to use a 0.13.x.

> I guess there's a chunk of code which is the "server shell" containing
> server single-instancing/startup and listening on a port and
> marshaling/demarshaling protocol, and this chunk of code would ideally
> be independent of sbt itself similar to how the launcher is - a sort
> of "sbt container" - maybe that's the first chunk of code? Want to
> write down the xsbti.Server idea?
>
> Network protocol seems like the next key question on the path to a
> minimal setup...

From what I see, network protocol is an implementation detail that can be thrown in an easily replaceable component except for one important aspect. That aspect is custom communication with a task. This is the part that we won't control. So, I think that if we want to deal with this now, yes, we need to talk about the protocol. If not, I think do whatever works now and it can be replaced later.

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zDzKU2HL%2BeJx%3DDdd-xYjZVM-rQ%3DyGSZu4iex7pNd5n6bA%40mail.gmail.com.

Havoc Pennington

unread,
Oct 9, 2013, 12:20:25 PM10/9/13
to sbt...@googlegroups.com
On Wed, Oct 9, 2013 at 11:51 AM, Mark Harrah <dmha...@gmail.com> wrote:
> I know Josh likes to put the hard problems up front to fail fast. I'm ok with both of these (although they do conflict a bit), but one important thing is to ensure no one gets blocked, since there are several of us.
>

I'd say if we have problems that need a "feasibility spike" (is this
even possible to solve?) I agree with Josh, but once we feel OK about
feasibility I personally dislike coding in a vacuum. It's better to
make a real, dogfoodable (but simple) thing and evolve it. But I think
Josh is going to work on this particular project way more than me, so
up to him. :-)

> To avoid blocking, I think we'll need to identify a general roadmap with dependencies between tasks and priorities. I'll take a shot at this.

Cool.

> I was thinking we should defer this, at least if you mean supporting 0.13 in general and not specifically the client-server setup.

My hope here was that the build server startup, location, and protocol
code could be getting an immediate workout in Activator (and soon
Eclipse hopefully), even prior to sbt.next. We could discover problems
earlier before sbt itself even relies on this "container" code.

I didn't mean here any changes to sbt 0.13 itself, rather that we'd
use some hack (based on sbt-remote-control's probe stuff most likely)
to shove 0.13 into the basic outlines of the new setup.

There is an alternative possible approach to the problem of "how do we
get from 0.13 to .next" where on the *client* side we'd have a jar
which essentially chooses between sbt-remote-control and sbt.next
server, using sbt-remote-control more or less as-is.

> From what I see, network protocol is an implementation detail that can be thrown in an easily replaceable component except for one important aspect. That aspect is custom communication with a task. This is the part that we won't control. So, I think that if we want to deal with this now, yes, we need to talk about the protocol. If not, I think do whatever works now and it can be replaced later.

I guess "protocol" is a somewhat overloaded word. Some of the elements:

* is it a bunch of discrete requests (REST) or a persistent
full-duplex socket (like WebSocket); this impacts for example whether
you can know the peer is gone
* the actual wire serialization format
* what are the kinds of messages (requests, replies, events for example)
* the protocol in the "URL" sense of protocol (http:, ws:)
* the specific "payloads" in the messages (specific types of requests
and events, possibly invented per-task)

I was just thinking of a sketch of some of this, will write it down. I
think the wire serialization format is the easiest to change later.

Havoc

Havoc Pennington

unread,
Oct 9, 2013, 12:28:41 PM10/9/13
to sbt-dev
On Wed, Oct 9, 2013 at 11:16 AM, Mark Harrah <dmha...@gmail.com> wrote:
> One more process thing for docs: how should we deal with changes? I like that it is on the wiki because it is tied to the project, but it doesn't do comments/replies like Google Docs. Should we have an owner for each document and do comments/discussions here and the owner is responsible for accepting changes and updating? Have a separate project and open issues for each discussion point? Something else?
>

Hmm. I guess the goal is that you, and whoever is coding a given
feature, agree on the plan. Sometimes this might need a detailed doc
and sometimes not.

How about:

- completely different proposed solution: create a new section in the
document, or a new page, and write it out from scratch. Send email
about it to start discussion between options.
- minor tweaks: suggest them via email, original author of the
document or section incorporates them

?

I don't think we need to make this a gratuitous amount of
specification work, it's just that getting synced up initially is
tough. Hopefully some of the questions can be kicked down the road a
bit.

Havoc

Mark Harrah

unread,
Oct 9, 2013, 1:50:35 PM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 12:28:41 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> On Wed, Oct 9, 2013 at 11:16 AM, Mark Harrah <dmha...@gmail.com> wrote:
> > One more process thing for docs: how should we deal with changes? I like that it is on the wiki because it is tied to the project, but it doesn't do comments/replies like Google Docs. Should we have an owner for each document and do comments/discussions here and the owner is responsible for accepting changes and updating? Have a separate project and open issues for each discussion point? Something else?
> >
>
> Hmm. I guess the goal is that you, and whoever is coding a given
> feature, agree on the plan. Sometimes this might need a detailed doc
> and sometimes not.

Yes, I expect we should all have a general consensus on the general direction, although it will evolve.

> How about:
>
> - completely different proposed solution: create a new section in the
> document, or a new page, and write it out from scratch. Send email
> about it to start discussion between options.
> - minor tweaks: suggest them via email, original author of the
> document or section incorporates them

Fine by me.

> ?
>
> I don't think we need to make this a gratuitous amount of
> specification work, it's just that getting synced up initially is
> tough. Hopefully some of the questions can be kicked down the road a
> bit.

I want to push details out as well, but I think a high-level view of how things will be solved will prevent duplicated or wasted work. We probably agree here.

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zBaqrBnBAArr_KaO81Q%3D37kD7zDwHu2mfYGxNJuf4nfVg%40mail.gmail.com.

Mark Harrah

unread,
Oct 9, 2013, 1:52:49 PM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 12:20:25 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> On Wed, Oct 9, 2013 at 11:51 AM, Mark Harrah <dmha...@gmail.com> wrote:
> > I know Josh likes to put the hard problems up front to fail fast. I'm ok with both of these (although they do conflict a bit), but one important thing is to ensure no one gets blocked, since there are several of us.
> >
>
> I'd say if we have problems that need a "feasibility spike" (is this
> even possible to solve?) I agree with Josh, but once we feel OK about
> feasibility I personally dislike coding in a vacuum. It's better to
> make a real, dogfoodable (but simple) thing and evolve it. But I think
> Josh is going to work on this particular project way more than me, so
> up to him. :-)

Ok, fine by me.

> > To avoid blocking, I think we'll need to identify a general roadmap with dependencies between tasks and priorities. I'll take a shot at this.
>
> Cool.
>
> > I was thinking we should defer this, at least if you mean supporting 0.13 in general and not specifically the client-server setup.
>
> My hope here was that the build server startup, location, and protocol
> code could be getting an immediate workout in Activator (and soon
> Eclipse hopefully), even prior to sbt.next. We could discover problems
> earlier before sbt itself even relies on this "container" code.
>
> I didn't mean here any changes to sbt 0.13 itself, rather that we'd
> use some hack (based on sbt-remote-control's probe stuff most likely)
> to shove 0.13 into the basic outlines of the new setup.

Ok, makes sense.

> There is an alternative possible approach to the problem of "how do we
> get from 0.13 to .next" where on the *client* side we'd have a jar
> which essentially chooses between sbt-remote-control and sbt.next
> server, using sbt-remote-control more or less as-is.
>
> > From what I see, network protocol is an implementation detail that can be thrown in an easily replaceable component except for one important aspect. That aspect is custom communication with a task. This is the part that we won't control. So, I think that if we want to deal with this now, yes, we need to talk about the protocol. If not, I think do whatever works now and it can be replaced later.
>
> I guess "protocol" is a somewhat overloaded word. Some of the elements:
>
> * is it a bunch of discrete requests (REST) or a persistent
> full-duplex socket (like WebSocket); this impacts for example whether
> you can know the peer is gone
> * the actual wire serialization format
> * what are the kinds of messages (requests, replies, events for example)
> * the protocol in the "URL" sense of protocol (http:, ws:)
> * the specific "payloads" in the messages (specific types of requests
> and events, possibly invented per-task)

Ok, I think we actually agree, I just focused on "network" and thought it was more about low-level implementation details (wire serialization format). It sounds like this is more like interactions/flow, which is what I want to see described as well.

> I was just thinking of a sketch of some of this, will write it down. I
> think the wire serialization format is the easiest to change later.

Sounds good.

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zA%2BRNh%3D4F1emEEvHH8Pjn9qcVVfoU-%2B1C8-XjqYCBvKnw%40mail.gmail.com.

Mark Harrah

unread,
Oct 9, 2013, 2:31:58 PM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 10:48:43 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> I added an example link to a solution discussion for the first problem
> (startup/discovery/lifecycle). (That link is to
> https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle )

I added a section on the lock service provided by the launcher here:

https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-2-some-other-idea-here

-Mark

Josh Suereth

unread,
Oct 9, 2013, 2:37:26 PM10/9/13
to sbt...@googlegroups.com
One question on the locking suggestion:

Do clients need to lock when attempting to read the current port?   Should they just always read in the active.properties file and look for a port or issue a request to the launcher to go launch the server?  I assume that's what is implied, but wanted to clarify.


--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Mark Harrah

unread,
Oct 9, 2013, 2:42:40 PM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 14:37:26 -0400
Josh Suereth <joshua....@gmail.com> wrote:

> One question on the locking suggestion:
>
> Do clients need to lock when attempting to read the current port? Should
> they just always read in the active.properties file and look for a port or
> issue a request to the launcher to go launch the server? I assume that's
> what is implied, but wanted to clarify.

I didn't think through it fully, mainly just to give you guys an idea of what you can expect from the facility. However, I'd say treat it as a synchronized {} block where you read/write the file (active.properties?) knowing you have exclusive access (assuming everyone else uses the locking facility of course). So, yes, always lock when reading/writing active.properties or whatever file contains the coordination information. In my pseudo-code, firing up the server was also done under lock. This avoids having another client read a port for a server that isn't ready yet or things like that.

-Mark

>
> On Wed, Oct 9, 2013 at 2:31 PM, Mark Harrah <dmha...@gmail.com> wrote:
>
> > On Wed, 9 Oct 2013 10:48:43 -0400
> > Havoc Pennington <h...@typesafe.com> wrote:
> >
> > > I added an example link to a solution discussion for the first problem
> > > (startup/discovery/lifecycle). (That link is to
> > > https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle )
> >
> > I added a section on the lock service provided by the launcher here:
> >
> >
> > https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-2-some-other-idea-here
> >
> > -Mark
> >
> > --
> > You received this message because you are subscribed to the Google Groups
> > "sbt-dev" group.
> > To unsubscribe from this group and stop receiving emails from it, send an
> > email to sbt-dev+u...@googlegroups.com.
> > To view this discussion on the web visit
> > https://groups.google.com/d/msgid/sbt-dev/20131009143158.38031c3e%40gmail.com
> > .
> > For more options, visit https://groups.google.com/groups/opt_out.
> >
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAFLqJkwJ%2B223BDQMA9%2BAMdA_Q9WXV1nBYOEP5%2BvMw9Ws6es27Q%40mail.gmail.com.

Josh Suereth

unread,
Oct 9, 2013, 2:53:39 PM10/9/13
to sbt...@googlegroups.com
On Wed, Oct 9, 2013 at 2:42 PM, Mark Harrah <dmha...@gmail.com> wrote:
On Wed, 9 Oct 2013 14:37:26 -0400
Josh Suereth <joshua....@gmail.com> wrote:

> One question on the locking suggestion:
>
> Do clients need to lock when attempting to read the current port?   Should
> they just always read in the active.properties file and look for a port or
> issue a request to the launcher to go launch the server?  I assume that's
> what is implied, but wanted to clarify.

I didn't think through it fully, mainly just to give you guys an idea of what you can expect from the facility.  However, I'd say treat it as a synchronized {} block where you read/write the file (active.properties?) knowing you have exclusive access (assuming everyone else uses the locking facility of course).  So, yes, always lock when reading/writing active.properties or whatever file contains the coordination information.  In my pseudo-code, firing up the server was also done under lock.  This avoids having another client read a port for a server that isn't ready yet or things like that.



In the event that the client is not a java-process, it'd be nice if we could just write-lock the file.   E.g. if the only thing that has to lock it is the "launcher".   That way, clients can try to ping whatever is listed, and failing that, issue a start request.  Then, spin-lock waiting for the server to be active.   In the event this launches a bunch of sbt launchers for the server, then after the first one is started, the rest will detect this and seppuku.  The clients will connect to the first listed port they find, and we're in pretty good shape.

We still need to have clients *and* server poll the active.properties randomnly (or upon network failure?) to ensure we only have one instance.   I don't think this changes that too much....

Havoc Pennington

unread,
Oct 9, 2013, 3:44:38 PM10/9/13
to sbt-dev
So there are two ways we could use locking I guess:

* make the whole ping/listen/write-active-file sequence atomic, vs.
with atomic file replacement we only make the write-active-file
atomic.
* hold lock for the lifetime of the server (try to use locking to
prevent multiple servers)

The second one is where I feel like OS locks could cause us headaches,
vs. a design that just sort of avoids the need for locking.

I didn't suggest a lockfile around ping/listen/write-active-file
because I *think* it's just an optimization (prevents some extra work
sometimes), but I could be wrong. If the sbt launcher lock facility
has already wrestled some of the OS lock headaches to the ground, then
it's much more appealing than if we had not already grappled them.

Re: "API for File.createNewFile says it shouldn't be used for file
locking (don't know why)" - I assumed they were talking about using it
to create a lock file setup, something like "if foo.lock gets created
by you then you have the lock," vs. F_SETLK where you say "if you have
an F_SETLK lock on the lockfile then you have the lock." In the
createNewFile solution there's no theoretically-sound way to avoid
stuck locks (nothing guarantees foo.lock deletion), etc. But this has
to be weighed against F_SETLK not actually working as expected all the
time.

Havoc

Mark Harrah

unread,
Oct 9, 2013, 4:59:26 PM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 15:44:38 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> So there are two ways we could use locking I guess:
>
> * make the whole ping/listen/write-active-file sequence atomic,

This was the way I understood and I see that this wasn't the way everyone else was thinking.

> vs.
> with atomic file replacement we only make the write-active-file
> atomic.

The API docs for File.renameTo say it isn't guaranteed to be atomic. I assume it will be atomic in typical situations (probably the same ones that locking works), but I'm not sure it would make things easier for NFS, for example. It might not matter that much in practice that it isn't atomic, but it might also fail badly in situations it does matter. As for Windows v. Linux, I believe renames on Windows are as atomic as on Linux.

A somewhat related point of interest is the appending can be atomic, but a bug fixed in Java 7 makes it not so on Windows:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6631352

> * hold lock for the lifetime of the server (try to use locking to
> prevent multiple servers)

I think you'd prevent multiple servers if the whole sequence in the first point is atomic too, right?

> The second one is where I feel like OS locks could cause us headaches,
> vs. a design that just sort of avoids the need for locking.
>
> I didn't suggest a lockfile around ping/listen/write-active-file
> because I *think* it's just an optimization (prevents some extra work
> sometimes), but I could be wrong. If the sbt launcher lock facility
> has already wrestled some of the OS lock headaches to the ground, then
> it's much more appealing than if we had not already grappled them.

The lock facility has at least been used for similar requirements: degree of contention and environment, mainly. For the atomic rename variant, say you have two servers start up at the same time-

> Re: "API for File.createNewFile says it shouldn't be used for file
> locking (don't know why)" - I assumed they were talking about using it
> to create a lock file setup, something like "if foo.lock gets created
> by you then you have the lock," vs. F_SETLK where you say "if you have
> an F_SETLK lock on the lockfile then you have the lock." In the
> createNewFile solution there's no theoretically-sound way to avoid
> stuck locks (nothing guarantees foo.lock deletion), etc. But this has
> to be weighed against F_SETLK not actually working as expected all the
> time.

That's probably it.

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zCmGwiE3HB5ZY2qKsLSV0mBZ96S%3DM5dZf7b-uqc58eDyQ%40mail.gmail.com.

Mark Harrah

unread,
Oct 9, 2013, 5:06:41 PM10/9/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 14:53:39 -0400
I might be confusing some things here, but I was assuming that the client would fork the launcher and the launcher handles this locking and ensures there is one instance. The launcher does this via the approach described in the pseudocode. The launcher would have to return information to the client, probably just the port, but I think Havoc listed some other information that would be in the active.properties file and that could be returned.

I'm not sure where the spin-lock comes in. I agree there might be a bunch of sbt launchers started up.

> We still need to have clients *and* server poll the active.properties
> randomnly (or upon network failure?) to ensure we only have one instance.
> I don't think this changes that too much....

I don't see why. Can you explain?

-Mark

> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAFLqJky3hn-u9qY3fWKMFJS0PXUVPNYtZaGr8nX52GsLsNzxtA%40mail.gmail.com.

Josh Suereth

unread,
Oct 9, 2013, 5:14:27 PM10/9/13
to sbt...@googlegroups.com
Ah, so *any client* who wants to communciate with the build server will execute the sbt-launcher.  This will be responsible for either:

(1) Determining if there is an existing server and returning the means of access
(2) Spinning up a server and returning the means of access.

So there's exactly *one* way to connect to a running server, forking the launcher.  Any other mechanism used to prevent more than one server, or lock is an implementation detail.

HOWEVER, shouldn't this forked process "detach" ?   i.e. we don't want the sbt build server to be killed (necessarily) when whatever client happened to ask where it is was killed, since there may be other clients connected.   We *do* want the server to be able to kill itself if there are no active clients (after a timeout).

If we can figure out a way to fork off the launcher, in a detached fashion, such that the means to connect to the running server come back *and* we know when to stop caring about the forked process (we've passed off ownership), then things are gravy.


 
> We still need to have clients *and* server poll the active.properties
> randomnly (or upon network failure?) to ensure we only have one instance.
> I don't think this changes that too much....

I don't see why.  Can you explain?


This is about failures and network reboots.  If the network dies, I need to figure out how to reconnect. If the server goes unresponsive, I need to kill it and start a new one.   Clients should be aware of what to do when they experience network failures.   I believe with the right locking strategy we can prevent more than one build server from running at the same time, but IIRC, locking isn't a 100% solution, e.g. when the directory is an NFS directory....   I think we're assuming local file systems, not remote here.

Mark Harrah

unread,
Oct 11, 2013, 10:09:23 AM10/11/13
to sbt...@googlegroups.com
On Wed, 9 Oct 2013 17:14:27 -0400
Yes, the assumption is that the first launcher forks another one if it needs to start a new server. I guess you could instead make a separate jar that just handles this server startup and reuses the locking facility. It would still need to fork. But, we might as well do this once and not have different clients have to reimplement this functionality.

> > > We still need to have clients *and* server poll the active.properties
> > > randomnly (or upon network failure?) to ensure we only have one instance.
> > > I don't think this changes that too much....
> >
> > I don't see why. Can you explain?
> >
> >
> This is about failures and network reboots. If the network dies, I need to
> figure out how to reconnect. If the server goes unresponsive, I need to
> kill it and start a new one. Clients should be aware of what to do when
> they experience network failures. I believe with the right locking
> strategy we can prevent more than one build server from running at the same
> time, but IIRC, locking isn't a 100% solution, e.g. when the directory is
> an NFS directory.... I think we're assuming local file systems, not
> remote here.

I'm not sure where the network comes in. If you mean the client<->server connection, that is over loopback, not an external network. In any case, if the client can't talk to the server, my understanding was that it just starts up a new one, which is covered by locking.

-Mark

> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAFLqJkxvYV6pkRiftBDL9ZLPxcVBExpWr9ckEqejTJcD5%3DH%2BHQ%40mail.gmail.com.

Havoc Pennington

unread,
Oct 11, 2013, 10:20:35 AM10/11/13
to sbt-dev
It seems like a simple option here would be to just try the launcher
lock facility to hold a lock for the lifetime of the server, and if
that turns out to break, get more creative.

Havoc

Mark Harrah

unread,
Oct 11, 2013, 10:28:21 AM10/11/13
to sbt...@googlegroups.com
Can you explain how this would work? As I see it, you'd still need a short-term lock on another file. Otherwise, no other clients could read what port the server is running on because the file would be locked.

I don't think the startup-only lock will be any more work than the solutions proposed so far. Each has to deal with forking/detaching, locking, etc...

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zA7Yb%2B4cfkmUAMGd3AMGP78HzQ_tWnEZb5G_BEGDk4Rtg%40mail.gmail.com.

Havoc Pennington

unread,
Oct 11, 2013, 10:46:52 AM10/11/13
to sbt-dev
On Fri, Oct 11, 2013 at 10:28 AM, Mark Harrah <dmha...@gmail.com> wrote:
>
> Can you explain how this would work? As I see it, you'd still need a short-term lock on another file. Otherwise, no other clients could read what port the server is running on because the file would be locked.
>
> I don't think the startup-only lock will be any more work than the solutions proposed so far. Each has to deal with forking/detaching, locking, etc...
>

Yeah, I think you're right, I just sort of lost track of the conversation.

Havoc

Josh Suereth

unread,
Oct 15, 2013, 1:13:32 PM10/15/13
to sbt...@googlegroups.com
So, I create a new (and not as fleshed out as hoped) proposal for leveraging the sbt launcher to do server startup: https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above

The idea here is that clients who need to start a server just fork the sbt-launcher (detached) and it'll notify them where the current server is (or become the current server).   Also, we should allow servers to open multiple ports and notify clients which ones are in use via the active.properties.

In any case, I normally think through things via discussion, so I'm posting here to see if anyone can help think through the issues and whether or no it's a good idea.



Havoc

--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Mark Harrah

unread,
Oct 15, 2013, 1:46:55 PM10/15/13
to sbt...@googlegroups.com
On Tue, 15 Oct 2013 13:13:32 -0400
Josh Suereth <joshua....@gmail.com> wrote:

> So, I create a new (and not as fleshed out as hoped) proposal for
> leveraging the sbt launcher to do server startup:
> https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above
>
> The idea here is that clients who need to start a server just fork the
> sbt-launcher (detached) and it'll notify them where the current server is
> (or become the current server). Also, we should allow servers to open
> multiple ports and notify clients which ones are in use via the
> active.properties.
>
> In any case, I normally think through things via discussion, so I'm posting
> here to see if anyone can help think through the issues and whether or no
> it's a good idea.

I agree with the listed assumptions, except I don't know why "network failure" is included. This should all be over the local loopback. That might technically be "network", but it isn't what you think of when you hear "network failure".

Some questions:

1. Why does the client care about open ports? It should just be given an exclusive port to talk to the server on.

2. Who consumes ServerMain and who implements it?

3. Is it necessary to have a shutdown service? It will be necessary to handle a bad shutdown- does a shutdown service gain anything?

-Mark

>
> On Fri, Oct 11, 2013 at 10:46 AM, Havoc Pennington <h...@typesafe.com> wrote:
>
> > On Fri, Oct 11, 2013 at 10:28 AM, Mark Harrah <dmha...@gmail.com> wrote:
> > >
> > > Can you explain how this would work? As I see it, you'd still need a
> > short-term lock on another file. Otherwise, no other clients could read
> > what port the server is running on because the file would be locked.
> > >
> > > I don't think the startup-only lock will be any more work than the
> > solutions proposed so far. Each has to deal with forking/detaching,
> > locking, etc...
> > >
> >
> > Yeah, I think you're right, I just sort of lost track of the conversation.
> >
> > Havoc
> >
> > --
> > You received this message because you are subscribed to the Google Groups
> > "sbt-dev" group.
> > To unsubscribe from this group and stop receiving emails from it, send an
> > email to sbt-dev+u...@googlegroups.com.
> > To view this discussion on the web visit
> > https://groups.google.com/d/msgid/sbt-dev/CAA3p8zAf_VHyAWCSZzj6Acdh-M8Tx%2Bx_dUBUVjwyNHR_Vi5Q2w%40mail.gmail.com
> > .
> > For more options, visit https://groups.google.com/groups/opt_out.
> >
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAFLqJkyRDyStitHMV7WX1EuHgyfBTX9i2ZSwPXuR7gJs98k%3DpA%40mail.gmail.com.

Josh Suereth

unread,
Oct 15, 2013, 1:57:00 PM10/15/13
to sbt...@googlegroups.com
On Tue, Oct 15, 2013 at 1:46 PM, Mark Harrah <dmha...@gmail.com> wrote:
On Tue, 15 Oct 2013 13:13:32 -0400
Josh Suereth <joshua....@gmail.com> wrote:

> So, I create a new (and not as fleshed out as hoped) proposal for
> leveraging the sbt launcher to do server startup:
> https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above
>
> The idea here is that clients who need to start a server just fork the
> sbt-launcher (detached) and it'll notify them where the current server is
> (or become the current server).   Also, we should allow servers to open
> multiple ports and notify clients which ones are in use via the
> active.properties.
>
> In any case, I normally think through things via discussion, so I'm posting
> here to see if anyone can help think through the issues and whether or no
> it's a good idea.

I agree with the listed assumptions, except I don't know why "network failure" is included.  This should all be over the local loopback.  That might technically be "network", but it isn't what you think of when you hear "network failure".

Some questions:

1. Why does the client care about open ports?  It should just be given an exclusive port to talk to the server on.


I think you can choose which port you want to use, e.g. HTTPS vs HTTP, or even which IP stack you connect on (IPv4 vs. IPv6).   You could also use ports to expose "old" protocols vs. new protocols.   I don't think "one port" is sufficient, although for the near term, we'll certainly only open one port.
 
2. Who consumes ServerMain and who implements it?

An artifact (like org.scala-sbt:sbt) would implement.  It knows how to ping for keep-alive, and how to bind the necessary ports it wants.   I'd like to use this for activator as well, where we only want to start up one-instance per-machine, but it would have similar requirements.

The consumer is the sbt-launcher, who is only responsible for ensuring that only one server is running at a time.
 

3. Is it necessary to have a shutdown service?  It will be necessary to handle a bad shutdown- does a shutdown service gain anything?


No.  It's just an optimisation for the next startup.  You can drop it entirely and the design remains unchanged.

Havoc Pennington

unread,
Oct 15, 2013, 2:05:24 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 1:13 PM, Josh Suereth <joshua....@gmail.com> wrote:
> So, I create a new (and not as fleshed out as hoped) proposal for leveraging
> the sbt launcher to do server startup:
> https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above
>

Cool, thoughts while reading:

* I think network failure = something is broken (i.e. 127.0.0.1 is
not really "network" in the usual sense; only possible error given
both processes are alive and bug-free should be broken OS or maybe
out-of-memory)
* in "isAlive(ServerPort[] ports)" what if one port is alive and one
isn't? what does multiple ports mean in general, anyway, that the
client has a choice?
* s/ServerPort/ServerAddress/ ? ... or - how about java.net.URI ?
* ShutdownService.shutdownServer I think is a handle for the server
to tell the launcher it wants to shut down, possible clearer name
maybe `ServerLauncher.notifyShuttingDown`, or something?
* will the launcher try to kill off the server if the lock file goes
away or no longer refers to the current server? how does it kill the
server if so?
* I suspect deleting the lockfile just causes trouble, if the ping
works reliably it shouldn't be needed - and it might hurt
* if we write out the pid in the lockfile, users could benefit from
knowing what to kill or being able to implement a "kill sbt now"
utility. In fact the file could have helpful doc comments or whatever.
* it might be nice if sbt 0.13 and sbt.next shared a library used to
implement ServerMain, because there's going to be a lot of code for
the server spawn, http listening, and pinging, which is logically
separate from the actual requests we'd send over the protocol. Sort of
a "container" vs. the sbt-version-specific "app"

Havoc

Mark Harrah

unread,
Oct 15, 2013, 2:12:32 PM10/15/13
to sbt...@googlegroups.com
On Tue, 15 Oct 2013 13:57:00 -0400
1. Each client is going to communicate to the server over its own exclusive port.

2. The server listens on a single port for new client requests.

3. These requests go through the server-launcher, so there is no need to handle diverse client connection types here.

4. When a request is received, the server starts up a new, random, unique port for the client.

5. The request can indicate old/new protocol or whatever.

6. There is no need to record the client ports in app, since they are not used again by the server-launching-service, right?


However, it occurs to me that I'm thinking in terms of a local socket based communication and you might be thinking REST-type communication. Is that right?

> > 2. Who consumes ServerMain and who implements it?
> >
>
> An artifact (like org.scala-sbt:sbt) would implement. It knows how to ping
> for keep-alive, and how to bind the necessary ports it wants. I'd like to
> use this for activator as well, where we only want to start up one-instance
> per-machine, but it would have similar requirements.
>
> The consumer is the sbt-launcher, who is only responsible for ensuring that
> only one server is running at a time.

The keep alive part is unclear to me, although see the comment about local socket v. REST.

> > 3. Is it necessary to have a shutdown service? It will be necessary to
> > handle a bad shutdown- does a shutdown service gain anything?
> >
> >
> No. It's just an optimisation for the next startup. You can drop it
> entirely and the design remains unchanged.

My guess is this is a minor gain- you probably save attempting to open a socket, but that should fail quickly. My vote is to not worry about this unless it demonstrates an actual improvement in practice.

-Mark

Mark Harrah

unread,
Oct 15, 2013, 2:18:57 PM10/15/13
to sbt...@googlegroups.com
On Tue, 15 Oct 2013 14:05:24 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> On Tue, Oct 15, 2013 at 1:13 PM, Josh Suereth <joshua....@gmail.com> wrote:
> > So, I create a new (and not as fleshed out as hoped) proposal for leveraging
> > the sbt launcher to do server startup:
> > https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above
> >
>
> Cool, thoughts while reading:

[...]

> * if we write out the pid in the lockfile, users could benefit from
> knowing what to kill or being able to implement a "kill sbt now"
> utility. In fact the file could have helpful doc comments or whatever.

In case you weren't already aware, there aren't any pid-related APIs in Java, just a bunch of hacks of varying quality.

> * it might be nice if sbt 0.13 and sbt.next shared a library used to
> implement ServerMain, because there's going to be a lot of code for
> the server spawn, http listening, and pinging, which is logically
> separate from the actual requests we'd send over the protocol. Sort of
> a "container" vs. the sbt-version-specific "app"

Agree.

-Mark

>
> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zD5yFv9i7qxkdLdBij6eCZVgV1SKCm0OGdxGDP9oQDV6A%40mail.gmail.com.

Havoc Pennington

unread,
Oct 15, 2013, 2:20:34 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 2:18 PM, Mark Harrah <dmha...@gmail.com> wrote:
> In case you weren't already aware, there aren't any pid-related APIs in Java, just a bunch of hacks of varying quality.

I know, just trying to make things easy for people. It can be hard to
figure out which java process is what from "ps"

Havoc

Josh Suereth

unread,
Oct 15, 2013, 2:27:48 PM10/15/13
to sbt...@googlegroups.com
On Tue, Oct 15, 2013 at 2:05 PM, Havoc Pennington <h...@typesafe.com> wrote:
On Tue, Oct 15, 2013 at 1:13 PM, Josh Suereth <joshua....@gmail.com> wrote:
> So, I create a new (and not as fleshed out as hoped) proposal for leveraging
> the sbt launcher to do server startup:
> https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above
>

Cool, thoughts while reading:

 * I think network failure = something is broken (i.e. 127.0.0.1 is
not really "network" in the usual sense; only possible error given
both processes are alive and bug-free should be broken OS or maybe
out-of-memory)

True.  
 
 * in "isAlive(ServerPort[] ports)" what if one port is alive and one
isn't? what does multiple ports mean in general, anyway, that the
client has a choice?

I think it's up to the client to decide....
 
 * s/ServerPort/ServerAddress/ ? ... or - how about java.net.URI ?

 Can we invent random "protocols" for java.net.URI? Or is it just URL that's odd?

 * ShutdownService.shutdownServer I think is a handle for the server
to tell the launcher it wants to shut down, possible clearer name
maybe `ServerLauncher.notifyShuttingDown`, or something?

Basically that's all we want.  A "hey go cleanup stuff while I die".
 
 * will the launcher try to kill off the server if the lock file goes
away or no longer refers to the current server? how does it kill the
server if so?

Hmm, probably.  And killing probably => System.exit (so you need shutdown hooks?)
 
 * I suspect deleting the lockfile just causes trouble, if the ping
works reliably it shouldn't be needed - and it might hurt

Why isn't locking the file needed?  How would it hurt?
 
 * if we write out the pid in the lockfile, users could benefit from
knowing what to kill or being able to implement a "kill sbt now"
utility. In fact the file could have helpful doc comments or whatever.

+1.
 
 * it might be nice if sbt 0.13 and sbt.next shared a library used to
implement ServerMain, because there's going to be a lot of code for
the server spawn, http listening, and pinging, which is logically
separate from the actual requests we'd send over the protocol. Sort of
a "container" vs. the sbt-version-specific "app"


Yeah, that makes a ton of sense.
 
Havoc

--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Mark Harrah

unread,
Oct 15, 2013, 2:28:23 PM10/15/13
to sbt...@googlegroups.com
Not that I disagree with making it easy and I'm not implying this is common enough knowledge, but:

jps -lmv

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zBPZP-2chAq%3D_zM9yod661UG4vQ_eY94bKEy1RrD81Cew%40mail.gmail.com.

Havoc Pennington

unread,
Oct 15, 2013, 2:28:52 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 2:12 PM, Mark Harrah <dmha...@gmail.com> wrote:
> However, it occurs to me that I'm thinking in terms of a local socket based communication and you might be thinking REST-type communication. Is that right?
>

If you listen for http on a port, you get both; when the client
connects and the socket is accepted, then the server can decide if
it's a REST path and the socket gets quickly closed, or the client and
server can negotiate a websocket and leave the socket open statefully.

I would say a continuously-open socket is needed in order to handle
lifecycle and events. With an open socket, the server gets
notification when the client goes away, and reliably has a client
count (without some kind of keep-alive timeout that would be needed in
a REST-only setup).

http supports continuously-open sockets via WebSocket, which is
nothing more than switching the http connection over to a
continuously-open link with bidirectional message streams.

So the server could support http://127.0.0.1/socket to open the
websocket, and also REST APIs such as htttp://127.0.0.1/ping to check
server aliveness, or http://127.0.0.1/ui for a primitive JS UI built
into the server, that kind of stuff.

So it isn't really REST or a socket, it's both, which http already supports.

I'm not sure there's an advantage to a server listening on multiple
ports if it's http-based. If it's not http-based then you might need a
port per protocol, or at least a port per
protocol-negotation-protocol.

Havoc

Josh Suereth

unread,
Oct 15, 2013, 2:33:03 PM10/15/13
to sbt...@googlegroups.com
Isn't this just part of port-negotiation?  Also, it may not be true, if, e.g. we expose a debug port.
 

2. The server listens on a single port for new client requests.

Yes. 

3. These requests go through the server-launcher, so there is no need to handle diverse client connection types here.

What does this mean?
 
4. When a request is received, the server starts up a new, random, unique port for the client.


?
 
5. The request can indicate old/new protocol or whatever.


True, but this just means you embed versioning in your protocol.
 
6. There is no need to record the client ports in app, since they are not used again by the server-launching-service, right?


Right, the only ports we expose here are those we are actively listening on for connections.   I'm not keeping UDP-style out of the mix for now, since we're on one machine.

 

However, it occurs to me that I'm thinking in terms of a local socket based communication and you might be thinking REST-type communication.  Is that right?

> > 2. Who consumes ServerMain and who implements it?
> >
>
> An artifact (like org.scala-sbt:sbt) would implement.  It knows how to ping
> for keep-alive, and how to bind the necessary ports it wants.   I'd like to
> use this for activator as well, where we only want to start up one-instance
> per-machine, but it would have similar requirements.
>
> The consumer is the sbt-launcher, who is only responsible for ensuring that
> only one server is running at a time.

The keep alive part is unclear to me, although see the comment about local socket v. REST.

> > 3. Is it necessary to have a shutdown service?  It will be necessary to
> > handle a bad shutdown- does a shutdown service gain anything?
> >
> >
> No.  It's just an optimisation for the next startup.  You can drop it
> entirely and the design remains unchanged.

My guess is this is a minor gain- you probably save attempting to open a socket, but that should fail quickly.  My vote is to not worry about this unless it demonstrates an actual improvement in practice.

Yeah, i think you're right here.  I'll drop it from the proposal. 

-Mark


--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Havoc Pennington

unread,
Oct 15, 2013, 2:34:27 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 2:27 PM, Josh Suereth <joshua....@gmail.com> wrote:
>> * in "isAlive(ServerPort[] ports)" what if one port is alive and one
>> isn't? what does multiple ports mean in general, anyway, that the
>> client has a choice?
>
>
> I think it's up to the client to decide....
>

I guess a client could decide which address to use, but the launcher
has to decide what "alive" means (all addresses work vs. any address
works) right.

> Can we invent random "protocols" for java.net.URI? Or is it just URL that's
> odd?

scala> new java.net.URI("mymadeupproto://whatevs")
res0: java.net.URI = mymadeupproto://whatevs

>> * will the launcher try to kill off the server if the lock file goes
>> away or no longer refers to the current server? how does it kill the
>> server if so?
>
>
> Hmm, probably. And killing probably => System.exit (so you need shutdown
> hooks?)

Oh, I forgot that there's no need to fork off the server. so yeah, just exit.
Shutdown hooks are basically always wrong since a design that requires
clean exit is wrong.

>> * I suspect deleting the lockfile just causes trouble, if the ping
>> works reliably it shouldn't be needed - and it might hurt
>
>
> Why isn't locking the file needed? How would it hurt?

It would hurt if we accidentally delete someone else's file, though
you're right this does mean locking would be broken.

Havoc

Josh Suereth

unread,
Oct 15, 2013, 2:36:43 PM10/15/13
to sbt...@googlegroups.com
We're having decent success using `jps` and `jps.exe` in activator.  Yes it's a hack, but it's like 80% for a nice improvement.

Josh Suereth

unread,
Oct 15, 2013, 2:40:59 PM10/15/13
to sbt...@googlegroups.com
On Tue, Oct 15, 2013 at 2:34 PM, Havoc Pennington <h...@typesafe.com> wrote:
On Tue, Oct 15, 2013 at 2:27 PM, Josh Suereth <joshua....@gmail.com> wrote:
>>  * in "isAlive(ServerPort[] ports)" what if one port is alive and one
>> isn't? what does multiple ports mean in general, anyway, that the
>> client has a choice?
>
>
> I think it's up to the client to decide....
>

I guess a client could decide which address to use, but the launcher
has to decide what "alive" means (all addresses work vs. any address
works) right.


No, the client does in the isAlive method.  It takes in all the addresses.  The "ServerApp" implementor can decide whether to check all, or just one.   The launcher just uses the result of isAlive.

 

>  Can we invent random "protocols" for java.net.URI? Or is it just URL that's
> odd?

scala> new java.net.URI("mymadeupproto://whatevs")
res0: java.net.URI = mymadeupproto://whatevs


rawk
 
>>  * will the launcher try to kill off the server if the lock file goes
>> away or no longer refers to the current server? how does it kill the
>> server if so?
>
>
> Hmm, probably.  And killing probably => System.exit (so you need shutdown
> hooks?)

Oh, I forgot that there's no need to fork off the server. so yeah, just exit.
Shutdown hooks are basically always wrong since a design that requires
clean exit is wrong.


Cool.  I'm thinking of things like Christopher's new JavaScript plugins which will fork off JS vms we can use to run JSLint and friends.  In this case, hopefully killing the sbt server will also kill the child process.
 
>>  * I suspect deleting the lockfile just causes trouble, if the ping
>> works reliably it shouldn't be needed - and it might hurt
>
>
> Why isn't locking the file needed?  How would it hurt?

It would hurt if we accidentally delete someone else's file, though
you're right this does mean locking would be broken.

I wasn't implying we delete the file, but the configuration in the file denoting our service.  It's just an optimisation.  We can just drop that for now, as it's uneeded for the prototype.

 
Havoc

--
You received this message because you are subscribed to the Google Groups "sbt-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.

Mark Harrah

unread,
Oct 15, 2013, 2:42:27 PM10/15/13
to sbt...@googlegroups.com
On Tue, 15 Oct 2013 14:33:03 -0400
Well, the whole thing is pretty much connection negotiation. As in Havoc's email, having one constant connection per client is useful for lifecycle management.

> Also, it may not be true, if, e.g. we expose a debug port.

I don't see why a debug port is special. Why wouldn't it just be a request to the server to open a new port?

> > 2. The server listens on a single port for new client requests.
> >
>
> Yes.
>
> >
> > 3. These requests go through the server-launcher, so there is no need to
> > handle diverse client connection types here.
> >
> > What does this mean?

The server-launcher-service and the server are the only endpoints on this socket. So, you don't need to worry about clients. They communicate any requirements to the server-launcher-service directly and the service then talks to the server over this socket. For example,

client:
<fork> java -jar launcherService.jar @app.boot.properties https v1.3
wait for it to return with the port (printed to stdout?)

launcherService:
withLock { ... }

> > 4. When a request is received, the server starts up a new, random, unique
> > port for the client.
> >
> >
> ?

launcherService to server: I have a request for a client to use https, protocol v1.3
server: listens on a new port N
server to launcherService: I'm listening on port N
launcherService to client: server is listening on port N for you

> > 5. The request can indicate old/new protocol or whatever.
> >
> >
> True, but this just means you embed versioning in your protocol.

Why?

> > 6. There is no need to record the client ports in app, since they are not
> > used again by the server-launching-service, right?
> >
> >
> Right, the only ports we expose here are those we are actively listening on
> for connections. I'm not keeping UDP-style out of the mix for now, since
> we're on one machine.

I still don't see why multiple ports need to be listened on for unsolicited connections.

-Mark

Josh Suereth

unread,
Oct 15, 2013, 2:49:11 PM10/15/13
to sbt...@googlegroups.com
If all the ports are listed in active.properties, I'm cool with this. Doesn't change the API at all, just how the client works, which I haven't outlined yet. I do like this mechanism.
 
> > 4. When a request is received, the server starts up a new, random, unique
> > port for the client.
> >
> >
> ?

launcherService to server: I have a request for a client to use https, protocol v1.3
server: listens on a new port N
server to launcherService: I'm listening on port N
launcherService to client: server is listening on port N for you


This *implies* you have a way for the client to talk to the launcher service and have him talk to the server.  If the server *DOESN'T* expose some protocol already, how do you communicate with it?   The design currently makes NO assumptions on higher-level protocols.  The *only* communication that can happen from client to server is via the active.properties file of active listening ports.  If you want a protocol open that allows you to request other ports, that's a different design (or a large adjustment to the current one).  I didn't want to make decisions on behalf of the server *how* the protocols/sockets are opened/served.

So, in the above, how is that communication happening? 

 
> > 5. The request can indicate old/new protocol or whatever.
> >
> >
> True, but this just means you embed versioning in your protocol.

Why?

See above about the assumed mechanisms and implicit protocol.  I'm trying to avoid such a thing, perhaps without good cause.
 

> > 6. There is no need to record the client ports in app, since they are not
> > used again by the server-launching-service, right?
> >
> >
> Right, the only ports we expose here are those we are actively listening on
> for connections.   I'm not keeping UDP-style out of the mix for now, since
> we're on one machine.

I still don't see why multiple ports need to be listened on for unsolicited connections.


If you design a protocol to request ports be opened, then perhaps you only leave that *one* protocol open by default and all other connections must first make a request for a valid port for their protocol.   This does place restrictions on how the server (and its protocols) are implemented.  I was trying to avoid imposing those restrictions.


Havoc Pennington

unread,
Oct 15, 2013, 2:51:09 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 2:40 PM, Josh Suereth <joshua....@gmail.com> wrote:
>
> No, the client does in the isAlive method. It takes in all the addresses.
> The "ServerApp" implementor can decide whether to check all, or just one.
> The launcher just uses the result of isAlive.
>

I think I'm completely confused about the purpose of isAlive here ;-)
and possibly about what processes you propose would exist.

On the wiki you wrote " The sbt launcher is responsible for ...
Checking to see if the old server is alive. If so, report these ports
to the client"

So when you say the client does isAlive, now I'm confused.

I thought we had:

Client code =>
Sbt launcher =>
Server Container =>
Server Implementation (sbt proper)

Is xsbti.ServerMain the launcher-container boundary or the
client-launcher boundary?

Is the client going to fork a process that runs the launcher, or load
the launcher jar into the client's process?

Where do we have process boundaries above in your proposal?

Havoc

Havoc Pennington

unread,
Oct 15, 2013, 2:54:45 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 2:42 PM, Mark Harrah <dmha...@gmail.com> wrote:
> client:
> <fork> java -jar launcherService.jar @app.boot.properties https v1.3
> wait for it to return with the port (printed to stdout?)
>

In C I've done this with a dedicated pipe (see 'man dbus-daemon',
--print-pid / --print-address) but in Java we might have to use TCP
for this? (blurgh)

The trouble with stdout is rogue debug printfs but maybe that trouble
can be lived with.

Havoc

Mark Harrah

unread,
Oct 15, 2013, 2:59:13 PM10/15/13
to sbt...@googlegroups.com
Yeah, I'm ok with dealing with those rogue printlns if it means avoiding sockets for a rather simple use case. The code should all be under our control, so we can be sure to send things to stderr. Worst case, we can save the real stdout for our direct use and then System.setOut(notReallyStdOut).

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zCn9GfaCrYUCfZ2-F3scf%2BeJGiq7%3DNNK4LzdFZQCxytuw%40mail.gmail.com.

Josh Suereth

unread,
Oct 15, 2013, 3:14:22 PM10/15/13
to sbt...@googlegroups.com
SO, the client forks the launcher.jar, always, to figure out how to connect to the server.

Here's how a connection to an already running server would look:

Client code =forks=>
   Sbt launcher =loads-in-jvm=>
      Server-Implementation.isAlive


Here's how it would look like if we had an old active.properties:

Client code =forks=>
   Sbt launcher =loads-in-jvm=>
      Server-Implementation -- isAlive
      Server-Implementation -- start


Basically, we instantiate the "shell" of the server (the ServerMain instance) always, but we don't call "start" unless we need to.  This lets us delegate the determination of  "aliveness" to the implementation.

The client is *always* forking an sbt-launcher against a given @server.properties, and then getting the URI back as a string or some such.  I think that's the simplest possible API we
can have.  If needed, the Launcher can even "cheat" and move System.out somewhere so that only the URIs are reported on the real System.out (fid = 1 or whatever)

Havoc Pennington

unread,
Oct 15, 2013, 3:33:50 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 3:14 PM, Josh Suereth <joshua....@gmail.com> wrote:
> SO, the client forks the launcher.jar, always, to figure out how to connect
> to the server.
>
> Here's how a connection to an already running server would look:
>
> Client code =forks=>
> Sbt launcher =loads-in-jvm=>
> Server-Implementation.isAlive
>
>
> Here's how it would look like if we had an old active.properties:
>
> Client code =forks=>
> Sbt launcher =loads-in-jvm=>
> Server-Implementation -- isAlive
> Server-Implementation -- start
>
>
> Basically, we instantiate the "shell" of the server (the ServerMain
> instance) always, but we don't call "start" unless we need to. This lets us
> delegate the determination of "aliveness" to the implementation.
>
> The client is *always* forking an sbt-launcher against a given
> @server.properties, and then getting the URI back as a string or some such.
> I think that's the simplest possible API we
> can have. If needed, the Launcher can even "cheat" and move System.out
> somewhere so that only the URIs are reported on the real System.out (fid = 1
> or whatever)

OK, above is what I think I had in mind also, so we were just getting
tangled up in words.

Havoc

Havoc Pennington

unread,
Oct 15, 2013, 4:27:28 PM10/15/13
to sbt-dev
On Tue, Oct 15, 2013 at 2:42 PM, Mark Harrah <dmha...@gmail.com> wrote:
>
> launcherService to server: I have a request for a client to use https, protocol v1.3
> server: listens on a new port N
> server to launcherService: I'm listening on port N
> launcherService to client: server is listening on port N for you
>

I guess what we're specifying in this scenario isn't *really* a
protocol in the http sense, but the entire "client-server contract."
For example, v1.3 might mean that the http server has paths /ping and
/socket that do such-and-such.
I could also imagine that a "client-server contract" might imply
multiple ports or protocols or even some kind of usage of the
filesystem. It's the totality of expectations that client and server
have for one another.

There are three places I can think of for feature/contract negotiation:
- selecting the server jar to run (used for 0.13 vs. .next perhaps);
this is determined by the project's build properties presumably (?)
- selecting the client-server contract used to talk to that server
(as in above, e.g. http with such-and-such paths); one server process
might support multiple client-server contracts
- within a contract, negotiating protocol details: for example after
the WebSocket is live, messages could be exchanged to configure what
we'll send over it. We could also do things here like ask "what's your
sbt version?" or "do you have the run task?" or whatever.

It could be like this, translate to Java as needed,

case class ContractId(name: String, version: Int) // better name?

public interface ServerMain {
public java.net.URI start(AppConfiguration configuration, ContractId
contract);
public boolean isAlive(java.net.URI uri);
}

(Or can the ContractId thing be part of AppConfiguration?)

It's perhaps mixing issues to perform client-server contract selection
via URI selection. That is, a URI represents a concrete instance
that's running that we can connect to. One server may have N URIs. But
a contract represents a conceptual kind of thing we might want to
connect to, which would have a URI only when concretely instantiated.
A client would want to pick the client-server contract it has in mind,
but it doesn't give a crap about the URI other than it needs to obtain
one. URIs can be created on demand (potentially even per-client),
while available contracts are a fixed property of a particular server
implementation.

I'm not sure. I think a lot of this thread boils down to terminology
confusion and such.

Mark, maybe one different implication of what you're proposing above
would be to move the "server discovery" file and aliveness checking
inside the server rather than outside it. So I *think* you might be
saying let's just have:

public interface ServerMain {
public java.net.URI getOrStart(AppConfiguration configuration,
ContractId contract);
}

with the look up existing server file / isAlive handling *inside*
getOrStart ? The advantage of this is that a server implementation
could support multiple contracts but only start up URIs associated
with those contracts on demand when they are actually wanted? The
server would also have the option to generate a fresh URI each time.

Another question, is the "server singleton-ification mechanism" (the
lock file, basically) part of the client-server contract, or is it a
separately-identified "protocol"? When do we want servers to be able
to coexist vs. exclude each other? I would think that all servers
which are basically sbt build servers, and not some other kind of
thing, should mutually exclude each other.

Havoc

Josh Suereth

unread,
Oct 15, 2013, 6:03:05 PM10/15/13
to sbt...@googlegroups.com
On Tue, Oct 15, 2013 at 4:27 PM, Havoc Pennington <h...@typesafe.com> wrote:
On Tue, Oct 15, 2013 at 2:42 PM, Mark Harrah <dmha...@gmail.com> wrote:
>
> launcherService to server: I have a request for a client to use https, protocol v1.3
> server: listens on a new port N
> server to launcherService: I'm listening on port N
> launcherService to client: server is listening on port N for you
>

I guess what we're specifying in this scenario isn't *really* a
protocol in the http sense, but the entire "client-server contract."
For example, v1.3 might mean that the http server has paths /ping and
/socket that do such-and-such.
I could also imagine that a "client-server contract" might imply
multiple ports or protocols or even some kind of usage of the
filesystem. It's the totality of expectations that client and server
have for one another.


True, but the above assumes there's some sort of communication (protocol) between the client + server *about* what protocols + ports are available.  I'm fine going this route, but I think it means we have at least 2 ports open at any time (assuming we have 1 client), and most likely 3 or 4 if we use standard protocols like HTTP.

The reason is, we need a "base level" protocol for the sbt-launcher to communicate client<->server on protocol requests from clients and port responses from servers.  We can encode this in a different protocol, but that means we make a decision what protocol we use for all possibly servers (I'm thinking activator as our 2nd client here).
 
There are three places I can think of for feature/contract negotiation:
 - selecting the server jar to run (used for 0.13 vs. .next perhaps);
this is determined by the project's build properties presumably (?)

Yeah, actually this should be determined by the launch configuration used.  The launch configuration *may* use a dynamic "grab the version from this file" setting, as sbt does currently.
 
 - selecting the client-server contract used to talk to that server
(as in above, e.g. http with such-and-such paths); one server process
might support multiple client-server contracts

This I think is the most important piece of the negotiation.  We either ignore this, and just keep URLs around forever and add new ones when we need to fix things, or design protocol negotiation in up front.
 
 - within a contract, negotiating protocol details: for example after
the WebSocket is live, messages could be exchanged to configure what
we'll send over it. We could also do things here like ask "what's your
sbt version?" or "do you have the run task?" or whatever.

I'm pretty sure this would be an implementation detail for a server instance.  i.e. the launcher's sole responsibility is getting the server started and getting clients to connect to running servers.  Once there's over-the-wire configuration, that's outside the scope of the launcher (IMHO).
 
It could be like this, translate to Java as needed,

case class ContractId(name: String, version: Int) // better name?

public interface ServerMain {
  public java.net.URI start(AppConfiguration configuration, ContractId
contract);
  public boolean isAlive(java.net.URI uri);
}


Not sure this design fits that well if we start down this path.  In fact, I think we need something more like:

public interface ServerMain {
  public ServerInstance start(AppConfiguration);
}

public interface ServerInstance {
  public java.net.URI requestAccess(ContractId contract);  // Or just "ProtocolId?"
}

for the API that servers implement.   The sbt launcher would be responsible for writing active.properties with the "low-level protocol" port that you use to request access to a supported application contract protocol.   All the details of that communication (has to be Socket for windows, right?) is hidden by the sbt launcher.  You have to go through this negotiation process to get a running instance.

 
(Or can the ContractId thing be part of AppConfiguration?)

It's perhaps mixing issues to perform client-server contract selection
via URI selection. That is, a URI represents a concrete instance
that's running that we can connect to. One server may have N URIs. But
a contract represents a conceptual kind of thing we might want to
connect to, which would have a URI only when concretely instantiated.
A client would want to pick the client-server contract it has in mind,
but it doesn't give a crap about the URI other than it needs to obtain
one. URIs can be created on demand (potentially even per-client),
while available contracts are a fixed property of a particular server
implementation.

Yeah, I think using URI is a misnomer.  All I wanted to expose was:  Port, Bound Address  + Protocol Enum.   Adding "resources" conflates things, and assumes HTTP technology.

 
I'm not sure. I think a lot of this thread boils down to terminology
confusion and such.

Mark, maybe one different implication of what you're proposing above
would be to move the "server discovery" file and aliveness checking
inside the server rather than outside it. So I *think* you might be
saying let's just have:

public interface ServerMain {
  public java.net.URI getOrStart(AppConfiguration configuration,
ContractId contract);
}

with the look up existing server file / isAlive handling *inside*
getOrStart  ? The advantage of this is that a server implementation
could support multiple contracts but only start up URIs associated
with those contracts on demand when they are actually wanted? The
server would also have the option to generate a fresh URI each time.


No, I think mark is implying the changes I show above.   I.e. the open port on the server is used to negotiate with the running server instance. i.e. sbt-launcher needs to know how
to run as a server and host its own protocol for communicating client-server requests (albeit a minimum).   I was hoping to isolate such comunication to just through the active.properties file.   Mark's suggestions need something more.  Maybe we do need that?  complicates implementation, simplifies design....
 
Another question, is the "server singleton-ification mechanism" (the
lock file, basically) part of the client-server contract, or is it a
separately-identified "protocol"? When do we want servers to be able
to coexist vs. exclude each other? I would think that all servers
which are basically sbt build servers, and not some other kind of
thing, should mutually exclude each other.


I'm thinking we can point the sbt-launcher at a particular lock-file. This would determine whether or not things can co-exists, whether or not they have the same lock file.

Havoc Pennington

unread,
Oct 15, 2013, 7:42:46 PM10/15/13
to sbt-dev
Hi,

I'm writing this top of the message after already working through some
details inline, so see below on those, but toplevel proposal is...

Why not make the launcher really simple. It just does the lock file
and it locates and singleton-ifies *one* URI:

ServerMain {
URI start(AppConfiguration)
boolean isAlive(URI)
}

The specific ServerMain implementation decides whether that URI is
some kind of meta-URI (where the client-server contract would involve
connecting to the root URI and negotiating some other URI instead).

The simplest ServerMain implementation is to listen on
http://127.0.0.1:5678 and return that as the URI, no choices for the
client.

Each server type just has to define what its "root" URI is, and how
one uses that root URI to locate any additional or dynamic or
negotiable URIs. Such negotiation could be in the client-server
contract and it's defined when you decide what ServerMain impl to
launch, at the same time that we figure out whether to launch 0.13 or
.next or whatever.

In the worst case, a server type could define that the URI is a data:
URI which contains arbitrary stuff, so any server we can imagine could
use this launcher. Or the URI could be a file: URI pointing to a file
with a list of other URIs in it. Or an http URI which returns a list
of stuff. Whatever - anything is possible.

I'd propose that initially our ServerMain impl just returns an http
URI, and we define what kinds of requests that http server will
handle, and that's it.

If we want to do something different we do it by selecting a different
ServerMain impl, just the same way we handle different sbt versions
with the launcher.

Or we extend the http server compatibly by saying there's a REST
request /shm which returns the fancy shared-memory transport's URI if
available, or NotFound otherwise. (Honestly for the sbt server I don't
even know what else we'd want to use besides http.)

This way we avoid trying to design negotiation of hypothetical future
stuff and just stick to picking a ServerMain impl to launch.
Future-ServerMain-impl which supports different ways to connect to the
same server process can be defined later. All that matters is that the
"root" URI matches what the client expected when selecting which
implementation to launch.

Inline stuff -

On Tue, Oct 15, 2013 at 6:03 PM, Josh Suereth <joshua....@gmail.com> wrote:
>> I could also imagine that a "client-server contract" might imply
>> multiple ports or protocols or even some kind of usage of the
>> filesystem. It's the totality of expectations that client and server
>> have for one another.
>>
>
> True, but the above assumes there's some sort of communication (protocol)
> between the client + server *about* what protocols + ports are available.
> I'm fine going this route, but I think it means we have at least 2 ports
> open at any time (assuming we have 1 client), and most likely 3 or 4 if we
> use standard protocols like HTTP.

I'd amend that slightly. It assumes there *can* be some sort of
communication about available protocols/ports. But the simplest
first-cut implementation is that the server only has one client-server
contract (the http one) and it's just hardcoded to return that one.

> The reason is, we need a "base level" protocol for the sbt-launcher to
> communicate client<->server on protocol requests from clients and port
> responses from servers. We can encode this in a different protocol, but
> that means we make a decision what protocol we use for all possibly servers
> (I'm thinking activator as our 2nd client here).

I don't think it makes sense to do this. To me this is a complexity
that would be "inside" a server implementation *if* that server
implementation wanted to dynamically add client-server contracts as
Mark suggested one could.

The simple server implementation is to have only one contract, or to
start up all contracts all the time, and in that case there's no need
for a contract-negotiation-protocol. It just reads the URI out of a
file or else says "nope, I don't have that client-server contract."

>> - within a contract, negotiating protocol details: for example after
>> the WebSocket is live, messages could be exchanged to configure what
>> we'll send over it. We could also do things here like ask "what's your
>> sbt version?" or "do you have the run task?" or whatever.
>>
> I'm pretty sure this would be an implementation detail for a server
> instance. i.e. the launcher's sole responsibility is getting the server
> started and getting clients to connect to running servers. Once there's
> over-the-wire configuration, that's outside the scope of the launcher
> (IMHO).

Yes, precisely! It's outside the scope of the launcher but it's still
a place that back compat and feature negotiation can be dealt with.

i.e. for most purposes, we *don't* need to change the big-picture
client-server contract, we just need to extend it.

The launcher-level contract negotiation would just be about some kind
of really big-picture change, like:
- we realize http was a mistake
- maybe using the ServerMain mechanism for non-sbt servers

I'd be very tempted to just not abstract any of this beyond the
launcher selecting which server to run, and then just don't break
things, or if we break things, handle it on the
launcher-selects-the-ServerMain-impl level.

If our client-server contract is basically that we have /ping which
returns OK, and we have /socket which opens a websocket, and then the
first thing on the socket is defined to be feature negotiation - then
we can throw all the complexity into the websocket protocol instead of
having complicated ways to get a URI. We can also extend by adding new
paths such as /fancyShmProtocol which will naturally return NotFound
if not supported.

>> It's perhaps mixing issues to perform client-server contract selection
>> via URI selection. That is, a URI represents a concrete instance
>> that's running that we can connect to. One server may have N URIs. But
>> a contract represents a conceptual kind of thing we might want to
>> connect to, which would have a URI only when concretely instantiated.
>> A client would want to pick the client-server contract it has in mind,
>> but it doesn't give a crap about the URI other than it needs to obtain
>> one. URIs can be created on demand (potentially even per-client),
>> while available contracts are a fixed property of a particular server
>> implementation.
>>
> Yeah, I think using URI is a misnomer. All I wanted to expose was: Port,
> Bound Address + Protocol Enum. Adding "resources" conflates things, and
> assumes HTTP technology.

That's not what I'm trying to say - I do think protocol+address+port
is a URI. URIs can also be data: or file: or whatever, and that's good
flexibility.

If I'm negotiating a client-server contract, I want to say "I'm going
to use the http setup with the /ping path and the /socket path, and
I'm going to expect such-and-such on /socket"
I want to tell the *kind* of root URI I expect.

The client wants to specify something like "sbt server setup version
1" or "version 2" or "version 3"

I do NOT want to say "I want to use any http-based thing you want, as
long as it's on 127.0.0.1 and port 5678"

i.e. I don't want to choose a contract by choosing a URI. The URI is a
specific server currently running which implements some client-server
contract. A URI can only be created once we *agree* on the contract -
if only a contract for negotiating further contracts ;-)

I don't think URIs are negotiated, they are just handles. The only
negotiation is about what kind of URI I want to get.

We could put that negotiation in two places:
- decide what to launch in the first place
- talk to the thing you launched, via its root/identity URI, and ask
it for more URIs

>> public interface ServerMain {
>> public java.net.URI getOrStart(AppConfiguration configuration,
>> ContractId contract);
>> }
>>
>> with the look up existing server file / isAlive handling *inside*
>> getOrStart ? The advantage of this is that a server implementation
>> could support multiple contracts but only start up URIs associated
>> with those contracts on demand when they are actually wanted? The
>> server would also have the option to generate a fresh URI each time.
>>
>
> No, I think mark is implying the changes I show above. I.e. the open port
> on the server is used to negotiate with the running server instance. i.e.
> sbt-launcher needs to know how
> to run as a server and host its own protocol for communicating client-server
> requests (albeit a minimum). I was hoping to isolate such comunication to
> just through the active.properties file. Mark's suggestions need something
> more. Maybe we do need that? complicates implementation, simplifies
> design....

To me it might make sense to *allow* servers to create URIs on the fly
in the design, but I'd seek to avoid requiring ourselves to implement
it today. It'd sure be easier to start with just one client-server
contract and minimal machinery.

>> Another question, is the "server singleton-ification mechanism" (the
>> lock file, basically) part of the client-server contract, or is it a
>> separately-identified "protocol"? When do we want servers to be able
>> to coexist vs. exclude each other? I would think that all servers
>> which are basically sbt build servers, and not some other kind of
>> thing, should mutually exclude each other.
>>
>
> I'm thinking we can point the sbt-launcher at a particular lock-file. This
> would determine whether or not things can co-exists, whether or not they
> have the same lock file.

Yes, as long as the singleton mechanism is in the generic launcher,
and not in the stuff behind ServerMain. Which IS what I was thinking,
but it wasn't clear to me that's what everyone is thinking.

Whew. It's so hard to describe code in words.

Maybe you should just code the simplest thing that could work and we
can go from there... ;-)

Havoc

Mark Harrah

unread,
Oct 16, 2013, 9:42:35 AM10/16/13
to sbt...@googlegroups.com
Ok, I wrote my version, which accomplishes:

client: I want the application defined by boot.properties to run in cwd and use version X of protocol N
server-service: the application is running and listening on port P

https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#sample-code

-Mark

> Havoc
>
> --
> You received this message because you are subscribed to the Google Groups "sbt-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sbt-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sbt-dev/CAA3p8zCAhDujKR4vXJ6%2B704jY-nnu_2McBh5revNRbZX%3DH%3D1mA%40mail.gmail.com.

Josh Suereth

unread,
Oct 16, 2013, 9:57:11 AM10/16/13
to sbt...@googlegroups.com
Ok, so some notes here:

(1) The "locking"/ensuring that we only have one server at a time is done via a separate JAR/separate process.  THe server process itself will never touch the lock file.
(2) We spin up servers via the regular sbt launching mechanism.  
(3) There is some base-level protocol from the server-service to any launched server  (this is the sticking point to me).
(4) You're creating a new port for each client.  We don't really need that unless we're on old UDP connections.  TCP/HTTP negotiate new ports automatically from a listening port.  So you can do the above with just a single open port.

So, what I think I may agree on (and actually can make things simpler):

1. The "server locator" process needs to launch the server in a fork, and should die after it reports the open port.
2. It seems, perhaps, that the server should not touch the lock file directly.


Combined with Havoc's API

trait ServerMain {
  def start(config: AppConfiguratoin): java.net.URI
}

We can have the launcher automatically dump STDERR/STDOUT into log files, and specifically write this URI out to the "Server locator".

I'll update my proposal accordingly, to reflect us converging on a design.

Josh Suereth

unread,
Oct 16, 2013, 10:30:14 AM10/16/13
to sbt...@googlegroups.com
Alright, I updated my proposal to take into account various ideas from each person.  Please let me know what you think now: https://github.com/sbt/sbt/wiki/Client-server-discovery-lifecycle#proposal-3-a-combination-of-the-above

Havoc Pennington

unread,
Oct 16, 2013, 11:11:21 AM10/16/13
to sbt-dev
Code is helpful, thanks.

Mark, my bigger-picture questions on your code:

- what's the goal of the intermediate server-provider process vs.
having this code in a library that runs in either the client process
or the server process?
- what would be in ConnectionConfiguration that could not be in
either boot.properties or done by talking to the server in its
native/final protocol

Smaller thing:

- s/port/uri/ in ServerInfo makes it more flexible

Josh's new code is what I was also thinking (I think), but I think
code could be added which would address some of what I think Mark is
trying to address (though I'm not sure I know all the goals).

Let me try to work through how I'd do what Mark is talking about given
Josh's launcher proposal -

In Josh's scenario, I think if one wanted a server that supported
ConnectionConfiguration, then you would get back the URI you would
send the ConnectionConfiguration to, from Josh's code. And then you
could proceed to do a configuration exchange and possibly get other
URIs based on it.

That is, the launched server *could* return
x-my-connection-config-proto://127.0.0.1:NNNN and then have code to
read/send ConnectionConfiguration just like in Mark's example when the
client connects to that. The server's URI doesn't have to be its only
URI, it can be a "bootstrap" URI. Expectations are defined by what the
client launches - by server.org/server.name in the boot.properties.

You could also launch a server-service-server which supported a
protocol for launching more servers.

But I'd suggest in our first simple cut at an implementation for
"server.org=org.scala-sbt, server.name=sbt," it's easier to return
http://127.0.0.1:NNNN.

Then different configurations could just be different resources on
that http server, or negotiated via http in one of several ways. HTTP
already has a bunch of ways to negotiate and extend:

- websocket is a negotiated protocol switch on the http connection,
and any other protocol in theory could be negotiated in the same way
- it's easy to have http://127.0.0.1:NNNN/get-the-foo-url which would
create & return a custom url using a non-http protocol
- http://127.0.0.1:NNNN/ or http://127.0.0.1:NNNN/something could
redirect to another non-http URL
- content types can be negotiated

We could use any of those to obtain custom ways to connect to the
server, so "bootstrapping" via http certainly does not mean that the
server would be http-only forever.

BUT the launcher mechanism Josh proposes *allows* launching a server
that supports a custom ConnectionConfiguration protocol, you would
just have to ask for that in your launch configuration.
You might do something like:

[server]
org=org.scala-sbt
name=serverService

And then the client would expect to get
x-my-connection-config-proto://127.0.0.1:NNNN as the URI and proceed
accordingly.

Havoc

Mark Harrah

unread,
Oct 16, 2013, 11:31:43 AM10/16/13
to sbt...@googlegroups.com
Yes.

> (2) We spin up servers via the regular sbt launching mechanism.

Yes.

> (3) There is some base-level protocol from the server-service to any
> launched server (this is the sticking point to me).

You could just as well write to the stdin of the launched server and it could write a line with the port back out. It is a protocol to the extent that Map[String,String] => Int is a protocol.

In order to determine if the server is running, you have to know something about the protocol. This is why your proposal has the isAlive method. This means the launcher has to update/retrieve/load the server classes in a new process every time a client wants to connect so it can ask isAlive. If you are ok with an additional ~1 s startup time for each connecting client, that might be fine. I don't think it is find for the command line client, which is already too slow to start up.

I don't think it makes sense to make the protocol flexible in your proposal: specify HTTP and some path /ping for isAlive as Havoc suggested and there is no need for the full launcher process.

> (4) You're creating a new port for each client. We don't really need that
> unless we're on old UDP connections. TCP/HTTP negotiate new ports
> automatically from a listening port. So you can do the above with just a
> single open port.

You could use a single port if you are ok moving negotiation into the client/server protocol. You still have to specify a base-level protocol for negotiating the real protocol+version or you specify that everyone uses the same REST API and does .../v1/... Either way, that negotiation is something that becomes a public API (between server+client) and can't change if you want things to work across versions (just restating what Havoc has already said I guess).

> So, what I think I may agree on (and actually can make things simpler):
>
> 1. The "server locator" process needs to launch the server in a fork, and
> should die after it reports the open port.
> 2. It seems, perhaps, that the server should not touch the lock file
> directly.

Yes.

> Combined with Havoc's API
>
> trait ServerMain {
> def start(config: AppConfiguratoin): java.net.URI
> }

Why is a URI necessary? It seems to me that the only unknown is the port. The host is always local and the protocol is fixed for a particular server.

> We can have the launcher automatically dump STDERR/STDOUT into log files,
> and specifically write this URI out to the "Server locator".

Log files are a possibility, although you lose relative ordering of the output. If that could be important, the server should handle it. I assume you want to do it in the launcher to capture any output from the launcher itself?

-Mark

Havoc Pennington

unread,
Oct 16, 2013, 11:43:01 AM10/16/13