client-server split for sbt.next

490 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
to sbt-dev
On Wed, Oct 16, 2013 at 11:31 AM, Mark Harrah <dmha...@gmail.com> wrote:
> 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.
>

There are java libs for using unix domain sockets, for example; or the
uri could be for a dbus service using a java-dbus lib; or a server
type could define protocol or host to be flexible; or we could say the
URI for a server is the "base URI" and is allowed to have a path in it
like http://127.0.0.1:NNNN/v1/ ; or a server could somehow be based on
a file or data URI, like you're supposed to read the file and load
contact info from it ... URI lets servers be defined in all kinds of
ways.

Havoc

Mark Harrah

unread,
Oct 16, 2013, 11:45:06 AM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 11:11:21 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> 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?

1) No need to hard code HTTP as the bootstrap protocol 2) no need to load the server's classes in every process 3) ideally a very lightweight server-provider process

I agree with your comments here and elsewhere that just hard coding HTTP as the bootstrap protocol is one way to go. It does mean everyone now loads an HTTP client on startup (mainly looking at the command line client, which wouldn't do that).

> - 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

I wouldn't put it in boot.properties. I'll elaborate at the end where you discuss that again.

You can do it in the native/final protocol. I agree with the pros/cons you've described.

> Smaller thing:
>
> - s/port/uri/ in ServerInfo makes it more flexible

How? The host is fixed (localhost) as is the protocol. The only case I can think of is where the protocol changes between server versions and the client either supports multiple protocols or bails. However, this means you can't use a newer server and an older client. You'd have to have the server listen on multiple ports with different protocols.
I agree that if you are going to make negotiation between the client/server directly, it makes sense to fix HTTP as the bootstrap protocol.

> 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.

The boot configuration is only read when the server is actually started and not when just connecting to an existing server. Right now, a server is identified by properties File+current working directory. If you had different properties files, you have to do something more of a semantic equality. That's not exceedingly hard, but I think it is more work than putting it either in the server<->service protocol or in the server<->client protocol.

-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/CAA3p8zDTQ7vtMhLL76HQdLe3tAR5K07BE-ekP2jjUVVhK%2BXtdw%40mail.gmail.com.

Mark Harrah

unread,
Oct 16, 2013, 11:47:35 AM10/16/13
to sbt...@googlegroups.com
I elaborated more in my reply to your other email. Basically, we are talking about the bootstrap URI, which has to remain fixed in order to work across client/server versions. Sorry for splitting the discussion on this, let's continue on the other thread.

-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/CAA3p8zBVS0MyGX%2B0jS%2B9U845_e0wsvHO4tLbu%2BiBFMS6v8wSNQ%40mail.gmail.com.

Josh Suereth

unread,
Oct 16, 2013, 11:52:27 AM10/16/13
to sbt...@googlegroups.com
On Wed, Oct 16, 2013 at 11:31 AM, Mark Harrah <dmha...@gmail.com> wrote:
On Wed, 16 Oct 2013 09:57:11 -0400
Josh Suereth <joshua....@gmail.com> wrote:
<snip/>

> > >
> > > Maybe you should just code the simplest thing that could work and we
> > > can go from there... ;-)
> >
> > 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
> >
> >
>
> 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.

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.


I actually think it does.  Perhaps we can agree to disagree here.  IF we have to consolidate, HTTP seems the right thing to consolidate on, with known resources.


When it comes to startup time, I agree that it's not ideal to reload the classes.  However, I don't see how this would slow down startup any further than it already is.   IF we lock down to an HTTP protocol with known ping resource, that's fine.  Otherwise, I think the hit is worth it.   That way the service can make more intelligent estimates as to whether or not it is down.

You'd also want to avoid having too many classes loaded in the "isAlive" method.  Not ideal, but I personally like it better than the alternative.  I'd be willing to drop it, if it's a sticking point.
 
> (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).


Yeah, I think negotiating on the client/server protocol is the right way to go.   We should relegate such decisions and protocol-decisions to the server itself.  You guys convinced me it's not a good idea to have the launcher/service-locater bake this in.

 
> 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 haven't really discussed binding to ::1 vs 0.0.0.0 vs. 127.0.0.1.   I think you still want to know the address (IPv6 or IPv4).

 
> 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?


Yeah.   I want a fail-safe to make sure the stdout isn't gunked up with non-protocol information.  I.e. STDOUT is our protocol between the service-locator and the server launcher.
 
-Mark

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

--
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.

Josh Suereth

unread,
Oct 16, 2013, 11:54:50 AM10/16/13
to sbt...@googlegroups.com
This is an assumption I don't think the rest of us shared, but is apparent in your code.   I'm not sure this restriction needs to be in place either.  Can you describe why it must be this?

Mark Harrah

unread,
Oct 16, 2013, 12:02:44 PM10/16/13
to sbt...@googlegroups.com
Sorry, do you mean the "it is more work" part or "is only read when the server is started" or "a server is identified by propertiesFile+cwd" or "semantic equality required"?

-Mark

Josh Suereth

unread,
Oct 16, 2013, 12:45:21 PM10/16/13
to sbt...@googlegroups.com
The "properties file + cwd" as the only things you have when connecting, and how you don't want to read the boot configuration except when starting a server.   I'm not sure why that's a restriction we have in place.  Is startup speed the concern?  It seems rather inhibiting.  Especially if I want to have different boot.properties refer to the same server.

E.g. We're planning to use this mechanism to launch activator as well, and ensure only one instance is running of the UI.  Then when you launch in a given directory, we can redirect you to the same server, but opening the app you wish.  The idea there is the "lock" file specification would be something like ~/.activator/active.properties, rather than in the cwd.

This is why I see propertiesFile + cwd as too restrictive in identifying a server.  You really want server.module + server.artifact (and maybe server.mainClass) as a hash denoting what type of server....  *or* you want to pass in where the lock file should be generated (vs. where the CWD is) when starting a server....



Mark Harrah

unread,
Oct 16, 2013, 12:52:37 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 11:52:27 -0400
This is related to the discussion of the bootstrap protocol/port. If you make the bootstrap flexible, you need to negotiate it- this opens up the negotiation issues again.

> When it comes to startup time, I agree that it's not ideal to reload the
> classes. However, I don't see how this would slow down startup any further
> than it already is.

Not sure what you mean here. The launcher+application just for isAlive is going to be ~1 s and that's not really going to go down easily. The code to ping/fork is specialized and is a lot easier to optimize if necessary.

> IF we lock down to an HTTP protocol with known ping
> resource, that's fine. Otherwise, I think the hit is worth it. That way
> the service can make more intelligent estimates as to whether or not it is
> down.

(I wouldn't propose dropping isAlive in favor of flakiness.)

> You'd also want to avoid having too many classes loaded in the "isAlive"
> method. Not ideal, but I personally like it better than the alternative.
> I'd be willing to drop it, if it's a sticking point.

Class loading guarantees from the jvm are only about correctness and not performance, so I don't really want to be worrying about whether my entry point, which is in the same codebase as the reset of my application, will load those other classes when isAlive is called.

> > > (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).
> >
> >
> Yeah, I think negotiating on the client/server protocol is the right way to
> go. We should relegate such decisions and protocol-decisions to the
> server itself. You guys convinced me it's not a good idea to have the
> launcher/service-locater bake this in.

Ok.

> > > 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 haven't really discussed binding to ::1 vs 0.0.0.0 vs. 127.0.0.1. I
> think you still want to know the address (IPv6 or IPv4).

Does this actually matter?

> > > 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?
> >
> >
> Yeah. I want a fail-safe to make sure the stdout isn't gunked up with
> non-protocol information. I.e. STDOUT is our protocol between the
> service-locator and the server launcher.

Ok, good point.

-Mark

> > -Mark
> >
> > > I'll update my proposal accordingly, to reflect us converging on a
> > design.
> >
> > --
> > 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/20131016113143.3a8efe8e%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/CAFLqJkwO0%3DBz9wKV3sPAk9a%3D5ZU4csNCKqKnPcNj8ufK%2BtJ-Bg%40mail.gmail.com.

Mark Harrah

unread,
Oct 16, 2013, 12:58:44 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 12:45:21 -0400
I see your point about the lock file. I have no problem passing where the lock file should be instead of it being computed.

I also see your point about boot.properties, but I think there is more to it. For the boot.properties file, you can't just look at the module+artifact+mainClass. You have to check that all of the other settings are the same. If they aren't, what do you do other than say "incompatible boot.properties files"? If you ignore the difference, the user wonders why their repositories aren't being respected. (Starting up separate servers wouldn't make sense either, but I don't think anyone is proposing that.) But, how do you get activator+IDE+command line to use the same boot.properties? I think that is the real problem whether or not the File is exactly the same.

-Mark

Josh Suereth

unread,
Oct 16, 2013, 1:04:14 PM10/16/13
to sbt...@googlegroups.com
On Wed, Oct 16, 2013 at 12:58 PM, Mark Harrah <dmha...@gmail.com> wrote:
On Wed, 16 Oct 2013 12:45:21 -0400
Josh Suereth <joshua....@gmail.com> wrote:
<snip/>

>
> The "properties file + cwd" as the only things you have when connecting,
> and how you don't want to read the boot configuration except when starting
> a server.   I'm not sure why that's a restriction we have in place.  Is
> startup speed the concern?  It seems rather inhibiting.  Especially if I
> want to have different boot.properties refer to the same server.
>
> E.g. We're planning to use this mechanism to launch activator as well, and
> ensure only one instance is running of the UI.  Then when you launch in a
> given directory, we can redirect you to the same server, but opening the
> app you wish.  The idea there is the "lock" file specification would be
> something like ~/.activator/active.properties, rather than in the cwd.
>
> This is why I see propertiesFile + cwd as too restrictive in identifying a
> server.  You really want server.module + server.artifact (and maybe
> server.mainClass) as a hash denoting what type of server....  *or* you want
> to pass in where the lock file should be generated (vs. where the CWD is)
> when starting a server....

I see your point about the lock file.  I have no problem passing where the lock file should be instead of it being computed.

I also see your point about boot.properties, but I think there is more to it.  For the boot.properties file, you can't just look at the module+artifact+mainClass.  You have to check that all of the other settings are the same.  If they aren't, what do you do other than say "incompatible boot.properties files"?  If you ignore the difference, the user wonders why their repositories aren't being respected.  (Starting up separate servers wouldn't make sense either, but I don't think anyone is proposing that.)  But, how do you get activator+IDE+command line to use the same boot.properties?  I think that is the real problem whether or not the File is exactly the same.



I think we can limit this down even further.  How about this assumption:  

  If the server is different, you will specify a different lock file.  If it's the same, the lock file location is the same.

Then, if you can specify lock files based on:  
* user directory + extras
* cwd of launcher/client + extras
* arbitrary location

we should be able to prevent multiple "similar" servers based on the lockfile.   E.g. if activator pushes a project to sbt 0.12.4 (for the hooks it needs), but the project/build.properties reads 0.12.3, we won't end up with two servers.  Activator will be notified that the server is already started.  We probably need some mechanism to report the version though.   perhaps we need to write out the launch properties into the "active.properties" file, so that clients can optionally cycle a server into a newer version, if desired.   That's an additional can of worms to think about.  Curious what your thoughts were here.





Josh Suereth

unread,
Oct 16, 2013, 1:14:13 PM10/16/13
to sbt...@googlegroups.com
On Wed, Oct 16, 2013 at 12:52 PM, Mark Harrah <dmha...@gmail.com> wrote:
On Wed, 16 Oct 2013 11:52:27 -0400
Josh Suereth <joshua....@gmail.com> wrote:
<snip/>

> > 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.
> >
> >
> I actually think it does.  Perhaps we can agree to disagree here.  IF we
> have to consolidate, HTTP seems the right thing to consolidate on, with
> known resources.

This is related to the discussion of the bootstrap protocol/port.  If you make the bootstrap flexible, you need to negotiate it- this opens up the negotiation issues again.


I'm claiming that negotiation issues are outside the scope of the sbt-launcher.  That's an implementation detail for each "server app".
 
> When it comes to startup time, I agree that it's not ideal to reload the
> classes.  However, I don't see how this would slow down startup any further
> than it already is.

Not sure what you mean here.  The launcher+application just for isAlive is going to be ~1 s and that's not really going to go down easily.  The code to ping/fork is specialized and is a lot easier to optimize if necessary.


Ok, I modified the proposal to assume HTTP + HEAD requests as the ping mechanism.  Should optimise the startup time by not requiring us to load classes.  If we work it right, we may also be able to avoid loading the configuration file if we know where the lock file should be.
 
> IF we lock down to an HTTP protocol with known ping
> resource, that's fine.  Otherwise, I think the hit is worth it.   That way
> the service can make more intelligent estimates as to whether or not it is
> down.

(I wouldn't propose dropping isAlive in favor of flakiness.)

Neither would I.  However, we can use HTTP HEAD requests in lieu of a specific ping mechanism.  It's an implicit contract which should allow for much lower amount of classes being used and far less startup time to determine if a server is active.  I updated my proposal appropriately. 


> You'd also want to avoid having too many classes loaded in the "isAlive"
> method.  Not ideal, but I personally like it better than the alternative.
>  I'd be willing to drop it, if it's a sticking point.

Class loading guarantees from the jvm are only about correctness and not performance, so I don't really want to be worrying about whether my entry point, which is in the same codebase as the reset of my application, will load those other classes when isAlive is called.


Yep.  Dropped that whole mechanism.

 <snip/>

Mark Harrah

unread,
Oct 16, 2013, 1:19:24 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 13:14:13 -0400
If the server returns a single URI to the client, it is fixing a bootstrap protocol/port and that's not negotiating. To be negotiated, there has to be a channel through the launcher (configuration file, base protocol, opens several ports for different protocols and returns List[URI], ...).

Agree with the rest below.

-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/CAFLqJky0DDQARXWpE_V1P044QDZvZh4PP83FUHSFHntBjSDA5w%40mail.gmail.com.

Josh Suereth

unread,
Oct 16, 2013, 1:31:38 PM10/16/13
to sbt...@googlegroups.com
I guess I'm now arguing that this negotiating doesn't need the launcher in the way.  Once the server is up, he has to have an HTTP protocol.  Any further negotiation is an implementation detail of that server, e.g. whether or not he opens more ports, or what not.   The only thing the launcher does is ensure he starts up, there's only one (per lock file) and you have an entry point to begin your negotiation.  Implicitly, there's a "HEAD" request you can make to check for "aliveness".   That's it.


Havoc Pennington

unread,
Oct 16, 2013, 1:31:44 PM10/16/13
to sbt-dev
Hi,

For speed, why not just a cheesy hack to try to grab the URI from the
file. Where Josh has:

def startServer(config: ServerConfiguration): URI = {
val bootProps = makeTemporaryBootProps(config)
val launcherJar = lookUpFromMyself()
val process = s"java -jar ${launcherJar} @${bootprops}".in(cwd).run
val uri = fromStdOut(process)
if(process isn't running) fail("Couldn't start server")
uri
}

Why not have the client-side library attempt to avoid the launcher
because it knows where the lockfile is for this particular server
type.

def startSpecificKindOfServerWeUnderstand(config: ServerConfiguration): URI = {
// here we assume we know or can figure out where the URI is stored
val uriOption = uriFromLockFile(config)
val uri = uriOption flatMap { uri =>
// here of course we assume we know it's an http URI, but
change accordingly
// for other kinds of server
try { httpGet(uri.toURL / "ping"); Some(uri) }
catch {
case NonFatal(e) => None
}
} getOrElse startServer(config)
}

So if the server is already running, that avoids starting a new process.

The hackiness is that in theory the lock file containing the URI is an
implementation detail of the launcher, but I don't think it's the kind
of hackiness that actually causes trouble.
It's a nice simple way to avoid the overhead of spawning the extra
process when the server is already running.

Havoc

Josh Suereth

unread,
Oct 16, 2013, 1:34:48 PM10/16/13
to sbt...@googlegroups.com
That could be a decent optimisation.   With the caveat that I'd prefer to issue a HEAD request on the URI rather then encoding a known resource location :)

Havoc Pennington

unread,
Oct 16, 2013, 1:39:43 PM10/16/13
to sbt-dev
On Wed, Oct 16, 2013 at 1:31 PM, Josh Suereth <joshua....@gmail.com> wrote:
> I guess I'm now arguing that this negotiating doesn't need the launcher in
> the way. Once the server is up, he has to have an HTTP protocol.

Or, couldn't we say, the server has to have whatever protocol (or set
of allowed protocols) that the server type is defined to have to have.

I guess you're saying HTTP because of the change you just made to make
the pinging generic rather than having an isAlive?

I think you could avoid requiring HTTP in a couple other ways:

- move the pinging stuff inside start() (so start is getOrStart)
- rather than a HEAD, just see if you can socket.connect() (requires
the URI to be a flavor of TCP socket though)

Though,
* I do think just hardwiring HTTP is as reasonable as just hardwiring
anything else
* I agree each server type has to define a set of allowed protocols
* I missed why we want to hardwire a set of allowed protocols for
*all* server types

Havoc

Havoc Pennington

unread,
Oct 16, 2013, 1:41:05 PM10/16/13
to sbt-dev
On Wed, Oct 16, 2013 at 1:34 PM, Josh Suereth <joshua....@gmail.com> wrote:
> That could be a decent optimisation. With the caveat that I'd prefer to
> issue a HEAD request on the URI rather then encoding a known resource
> location :)
>

Yes - or even you could just socket.connect and then socket.close,
without sending anything.
HEAD proves that the server is servicing requests, but connect is
enough to prove that it exists.
At least in the C socket API, if Java hasn't done anything screwy...

Havoc

Josh Suereth

unread,
Oct 16, 2013, 1:43:37 PM10/16/13
to sbt...@googlegroups.com
Yeah... i was thinking HEAD is better because we could catch "dead" servers that way.   You only need to open the socket and see if that's accepted, but HEAD gives you a chance to talk to the application and have it tell you it's in a recoverable state (or not respond meaining it should be killed).

Havoc Pennington

unread,
Oct 16, 2013, 1:45:41 PM10/16/13
to sbt-dev
On Wed, Oct 16, 2013 at 11:47 AM, Mark Harrah <dmha...@gmail.com> wrote:
> On Wed, 16 Oct 2013 11:43:01 -0400
> Havoc Pennington <h...@typesafe.com> wrote:
>
>> On Wed, Oct 16, 2013 at 11:31 AM, Mark Harrah <dmha...@gmail.com> wrote:
>> > 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.
>> >
>>
>> There are java libs for using unix domain sockets, for example; or the
>> uri could be for a dbus service using a java-dbus lib; or a server
>> type could define protocol or host to be flexible; or we could say the
>> URI for a server is the "base URI" and is allowed to have a path in it
>> like http://127.0.0.1:NNNN/v1/ ; or a server could somehow be based on
>> a file or data URI, like you're supposed to read the file and load
>> contact info from it ... URI lets servers be defined in all kinds of
>> ways.
>
> I elaborated more in my reply to your other email. Basically, we are talking about the bootstrap URI, which has to remain fixed in order to work across client/server versions. Sorry for splitting the discussion on this, let's continue on the other thread.
>

I think I see the confusion between us here.

I'm thinking the protocol might vary across *types of server*, not
across versions of the same type of server.

So sbt server would always have a compatible bootstrap URI.

But some-other-kind-of-server using the launcher could have whatever
kind of bootstrap URI it wants.

Josh for example was talking about singleton-ifying the activator UI
process with the same launcher.

Havoc

Mark Harrah

unread,
Oct 16, 2013, 1:46:12 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 13:31:38 -0400
We agree. At least as I understood him, Josh meant negotiating the bootstrap protocol and not fixing it to be HTTP. If he didn't mean that, we're all in agreement.

-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/CAFLqJkw6aHkGRb15PNpy5GqmJAEWVXhjmsS4BxdtuTdiXsYMAw%40mail.gmail.com.

Mark Harrah

unread,
Oct 16, 2013, 1:48:02 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 13:39:43 -0400
Havoc Pennington <h...@typesafe.com> wrote:

> On Wed, Oct 16, 2013 at 1:31 PM, Josh Suereth <joshua....@gmail.com> wrote:
> > I guess I'm now arguing that this negotiating doesn't need the launcher in
> > the way. Once the server is up, he has to have an HTTP protocol.
>
> Or, couldn't we say, the server has to have whatever protocol (or set
> of allowed protocols) that the server type is defined to have to have.
>
> I guess you're saying HTTP because of the change you just made to make
> the pinging generic rather than having an isAlive?

Yes.

> I think you could avoid requiring HTTP in a couple other ways:
>
> - move the pinging stuff inside start() (so start is getOrStart)
> - rather than a HEAD, just see if you can socket.connect() (requires
> the URI to be a flavor of TCP socket though)

Yes, agree.

> Though,
> * I do think just hardwiring HTTP is as reasonable as just hardwiring
> anything else
> * I agree each server type has to define a set of allowed protocols
> * I missed why we want to hardwire a set of allowed protocols for
> *all* server types

We are just hardwiring the bootstrap protocol as far as I know.

-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/CAA3p8zBwzgWmM3bj_NTX9w5M2f3KyY%2BO%2B5EEXRb9o7iCM8hwbg%40mail.gmail.com.

Josh Suereth

unread,
Oct 16, 2013, 1:47:49 PM10/16/13
to sbt...@googlegroups.com
Originally.  I've "seen the light".  Let's fixate on HTTP as the bootstrap protocol and go from there.   At least there's enough flexibility in the HTTP spec it's not a huge limitation in terms of what we can implement :)

Mark Harrah

unread,
Oct 16, 2013, 1:50:47 PM10/16/13
to sbt...@googlegroups.com
With the server-service being minimal and not requiring the launcher, it could in theory be a library called by a client. (We don't really want to duplicate this code in a client, right?) A possibly important reason to fork is that you deal with less file locking issues (OS+jvm). If the locking code is loaded in the same jvm as the client, that client has some restrictions it has to follow in terms of environment (class loading, file 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/CAFLqJky5TDe3bQ6sP%3D6me2Oox5cGqfwWuM7n%3Dx7RW13x338O2w%40mail.gmail.com.

Mark Harrah

unread,
Oct 16, 2013, 1:53:35 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 13:45:41 -0400
Right, but that URI would be fixed for that server type in advance. The client isn't using that returned URI except for the port (and maybe IPv6 v. 4?). Maybe it verifies it is as expected, but it won't negotiate a protocol. (This is of course somewhat moot now that we've selected HTTP.)

-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/CAA3p8zDzZbgR4VXWAbajDw69cDR7oNuJJ12V8gtn%3Dt9%3DuhPKog%40mail.gmail.com.

Mark Harrah

unread,
Oct 16, 2013, 1:54:05 PM10/16/13
to sbt...@googlegroups.com
On Wed, 16 Oct 2013 13:47:49 -0400
Ok, sounds good.

-Mark

Havoc Pennington

unread,
Oct 16, 2013, 1:55:59 PM10/16/13
to sbt-dev
On Wed, Oct 16, 2013 at 1:48 PM, Mark Harrah <dmha...@gmail.com> wrote:
>> Though,
>> * I do think just hardwiring HTTP is as reasonable as just hardwiring
>> anything else
>> * I agree each server type has to define a set of allowed protocols
>> * I missed why we want to hardwire a set of allowed protocols for
>> *all* server types
>
> We are just hardwiring the bootstrap protocol as far as I know.
>

I did mean bootstrap protocol here - each server type could have its
own bootstrap as long as the server type is allowed to customize the
"ping" step.

But assuming http always should work fine, and I have no objection to
it, other than it seemed easy to avoid if we wanted to.

Havoc

Luc Bourlier

unread,
Oct 17, 2013, 6:13:22 AM10/17/13
to sbt...@googlegroups.com
Hey all,

I caught up with this thread, but one of the things I don't quite understand is what you mean by 'type of server'.

I can see that we may want to have different protocol (message and channel) if the client is the command line or something like Scala IDE. But as there should be only one instance of SBT active at a time for a build, it would make sense to me that there is also only one server (part of the same VM), which support different protocols.

I'll make a diagram of what I understood of the proposals. It helps me understand.

Luc

Mark Harrah

unread,
Oct 17, 2013, 8:29:01 AM10/17/13
to sbt...@googlegroups.com
Hey Luc,

On Thu, 17 Oct 2013 03:13:22 -0700 (PDT)
Luc Bourlier <luc.bo...@typesafe.com> wrote:

> Hey all,
>
> I caught up with this thread, but one of the things I don't quite
> understand is what you mean by 'type of server'.
>
> I can see that we may want to have different protocol (message and channel)
> if the client is the command line or something like Scala IDE. But as there
> should be only one instance of SBT active at a time for a build, it would
> make sense to me that there is also only one server (part of the same VM),
> which support different protocols.

I think your understanding is correct:

* there is one application instance active (in this case app=sbt) and it provides a server in the same jvm

* there is a small library/program that a client uses to manage application instances
+ this ensures there is exactly one instance running
+ it provides the client with the URI where it can make initial contact with that server

* HTTP is the protocol for the server's URI
+ the details of the HTTP communication are not specified at this level
+ this is the initial protocol only: the client/server may agree to use another protocol after the initial communication

-Mark

> I'll make a diagram of what I understood of the proposals. It helps me
> understand.
>
> Luc
>
> Le mercredi 16 octobre 2013 19:55:59 UTC+2, Havoc Pennington a écrit :
> >
> > On Wed, Oct 16, 2013 at 1:48 PM, Mark Harrah <dmha...@gmail.com<javascript:>>
> > wrote:
> > >> Though,
> > >> * I do think just hardwiring HTTP is as reasonable as just hardwiring
> > >> anything else
> > >> * I agree each server type has to define a set of allowed protocols
> > >> * I missed why we want to hardwire a set of allowed protocols for
> > >> *all* server types
> > >
> > > We are just hardwiring the bootstrap protocol as far as I know.
> > >
> >
> > I did mean bootstrap protocol here - each server type could have its
> > own bootstrap as long as the server type is allowed to customize the
> > "ping" step.
> >
> > But assuming http always should work fine, and I have no objection to
> > it, other than it seemed easy to avoid if we wanted to.
> >
> > 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/5b6bb796-9774-4bc7-8a33-5cf9b27081cf%40googlegroups.com.

Havoc Pennington

unread,
Oct 17, 2013, 8:36:31 AM10/17/13
to sbt-dev
Hi,

On Thu, Oct 17, 2013 at 6:13 AM, Luc Bourlier <luc.bo...@typesafe.com> wrote:
> I caught up with this thread, but one of the things I don't quite understand
> is what you mean by 'type of server'.

If there's one thing to learn from this thread it's that none of us
mean the same things by the same words ;-)

But - what I meant was different daemon apps that might be launched by
the launcher, for example:

- an sbt server that builds a project
- the activator UI (which is a Play app that talks to the sbt server)
- some other daemon completely unrelated to sbt or activator

So your understanding is correct that there's only one SBT server - or
at least only one SBT server at a time for a given project.

Maybe there could be different versions of the SBT server which we
would select based on something (such as project/build.properties),
though. I don't know yes or no on that. I think we probably end up
with a client library which runs the launcher and figures out what
properties to give to the launcher.

The "type of server" in Josh's proposal is determined by the launch
configuration, so we have:

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

which leads the launcher to download and launch the sbt server, but
different org and name would lead it to download and launch some other
thing.

Conceptually, I think "type of lock" or "lock name" *could* be
distinct from "type of server" - that is, you could have multiple
servers which all share a lock such that you can only run one of those
servers at a time. This could make sense, for example, if different
versions of the activator UI or sbt server were implemented as
separate server types. But I don't think the current proposal
separates the lock identity from the server identity, so this is a
conceptual possibility, not what I think Josh is proposing.

Well, that is what I meant by "type of server" anyway...

Havoc
Reply all
Reply to author
Forward
0 new messages