The WASstServerAdaptor story and its (preliminary) end


Well, a developer’s day is full of victories and waterloos. So I’ve had my developer’s day today.

After I had discussed my Server startup problems related to ports already in use, Marten came up with an explanation for VAST’s behavior on the VAST Support Group:

The reason for this behaviour is located in the method: SstTcpTransport>>#basicOpenListeningSocket and it seems to be correctly handled by VASmalltalk.

In this method a socket option (“SOREUSEADDR”) is set to true (if the configuration of the tcp network allows it, which is normally the case of VASmalltalk).

Hmm. Even if it’s not incorrect, I still don’t like it.
So my first waterloo was that Marten proved me wrong: this is not a bug or wrong behavior – from a very technical standpoint. I still thinnk the server pretends to be ready when it is not, and I don’t like it if my software tells me lies right into my face, especially if that even costs me lots of time…

The thread at the discussion group then continues with suggestions like changing the setting Marten mentioned in some very low level method for binding to a Socket. That is of course very deep down and you never know what you break if you open ALL TCP/IP Sockets as exclusive listeners.

So I decided to stick with my workaround that I described in my initial post on the subject (the reworked one, of course). It ran great – on Windows.

So here was my second waterloo. When I had😄 packaged this and tried to start the same image twice on the Linux test machine, the Adaptor never left the #isStarting phase. The log contained lots of entries telling me we’re waiting for the adaptor to finish starting:

'2013-04-24 17:32:48,917: [INFO] Waiting for the Adapter to finish starting'
'2013-04-24 17:32:49,519: [INFO] Waiting for the Adapter to finish starting'
'2013-04-24 17:32:50,121: [INFO] Waiting for the Adapter to finish starting'
'2013-04-24 17:32:50,723: [INFO] Waiting for the Adapter to finish starting'
'2013-04-24 17:32:51,325: [INFO] Waiting for the Adapter to finish starting'

.. and so on. So the Socket behavior differs between Windows and Linux. On Windows, the retry loop was never run, the Adapter was not running immediately.

So I decided to extend my ugly workaround a little more with a retry count. I hate the code as it looks now, because it reminds me of really dark days in my programmer career. I’ll show it to you nevertheless, because it may be useful for others as well (I know, it’s not rocket science, but copying it from here is faster than thinking about it – you’re welcome!):

startServerAdaptor

	"self startServerAdaptor"

	|adaptor maxRetries retries|

	maxRetries := 5.
	retries := 1.

	[
		(adaptor := WASstServerAdaptor port: self port) start.
		[adaptor isStarting and: [retries <= maxRetries]] whileTrue: [
			EsLogManager info: ('Waiting for the Adapter to finish starting, retry count = %1' bindWith: retries asString).
			(Delay forSeconds: 0.5) wait.
			retries := retries +1.
			].
		adaptor isRunning ifFalse: [Error signal: ('Adaptor is not Running - maybe port %1 is already in use?' bindWith: self port asString)].
		EsLogManager info: 'WASStServerAdaptor started on port: ' , port asString]
			on: Error
			do: [:ex |
				EsLogManager
					error: 'Seaside Adaptor couldn''t start due to: ' , ex description.
				ex pass]

This is not a victory to be proud of, but one that will help me waste far less time lookinkg for phantom bugs. Here’s what happens in the Server ssh session when I start the image a second time:

UIProcess reportError: Adaptor is not Running - maybe port xxxxx is already in use?
 Dumping walkback to file: walkback.log

And the server process is immediately exiting. When I look into the application log I can see the server didn’t start because the port was in use. Great! Why not be happy about a small victory that may lay the ground for faster progress. It’s not much ado about nothing, and II’m pretty sure it pays back one day.

4 thoughts on “The WASstServerAdaptor story and its (preliminary) end

  1. Und gleich noch ein Nachtrag: das Verhalten von Linux und Windows bei der Reuse-Option ist unterschiedlich. Das betrifft sowohl das Erst-Bindung wie auch spätere Bindungs.

    Nach allem, was ich da gelesen habe, scheint mir die sinnvollste Lösung die zu sein, einfach einen http-request durchzufuehren und die Antwort entsprechend zu checken/definieren, so dass man sicherstellen kann, dass es aus der aktuellen VA Instanz kommt.

    Wenn dann natuerlich Load-Balancing dazukommt ….

  2. Ich antworte mal auf Deutsch: Das Problem mit dem Reuse ist überall prinzipiell vorhanden – in allen Sprachen und in den Dokumenten von Microsoft findet man einiges zu diesem Thema.

    Anscheinend sollte man (ich bin da sehr vorsichtig, denn es gibt verschiedene Szenarien) beim ersten Versuch, den Socket ohne reuse binden – das fuehrt entweder sofort zum Fehler oder man hat den Port.

    Problematisch wird das in dem Fall, wenn der Socket einfach noch nicht freigegeben ist und das kann passieren, obwohl ein eventuell vorheriger Serverprozess sich bereits beendet hat.

    Im obigen Versuch, würde die neue Instanz kein Bind beim Chance haben – und man muesste die Anwendung immer wieder starten – bis es funktioniert und das kann durchaus “lange” dauern (2 Minuten ? – hat auch was mit dem Verhalten der Clients zu tun).

    Um diese Situation zu vermeiden, benutzt man die Reuse Option – die Anwendung kann dann zwar bereits ein bind durchführen, aber unter ungunstigen Umständen kann es Probleme geben.

    Microsoft sieht in reuse ein grosses Tor fuer malware, denn ein Prozess kann ohne grosse Security ein bind mit resuse durchfuehren – und wenn der Hauptprozess sich verabschiedet, hat die Malware den Port. Ein weiterer Prozess mit resuse option kommt dann übrigens nicht mehr durch. Man kann nur 1x mit resuse binden.

    1. Hallo Marten,

      vielen Dank für die ausführlichen Hintergrundinfos. Das macht natürlich irgendwo Sinn: man stellt sich schon mal am Port an, und wartet, bis man zum Zuge kommt, also derjenige, der aktuell alles Pakete entgegennimmt, damit aufhört.

      Natürlich würde dann aber bei dem WASstServerAdaptor noch fehlen, dass er sich auch wirklich aktiv anstellt und lauscht, bis was zu ihm durchdringt. Oder aber, dass er immer wieder mal versucht, sich in den aktiven Zustand (isRunning=true) zu versetzen. Soweit ich das sehe, ist das beides nicht der Fall. Ersteres mag technisch nicht möglich sein, letzteres sicher schon.

      Dass das aber nicht passiert, lässt sich in einem VAST-Entwicklungsimage sehr einfach nachvollziehen: man startet zwei (oder mehr) Adapter auf dem selben Port und schaut sich mal an, wie sich die Sache im Seaside Control Panel darstellt. Einer ist grün (isRunning = true) , der andere rot (isRunning = false). Stoppt man nun den laufenden, wäre es sinnvoll, der andere würde das merken und sich aufschalten. Er bleibt aber Rot und ein Webbrowser bekommt auch keine Antwort vom VAST-Image.

      Aber ich erwarte soviel highly sophisticated Zeugs gar nicht. Ich möchte ja nur, dass mein Server startet, oder aber, wenn er nicht auf Anfragen antworten kann, sich gleich wieder beendet und mir entsprechende Log-Meldungen ausgibt. Dann kann ich mich kümmern und nach dem Rechten sehen. So, wie es jetzt ist, muss ich stets per Web-Browser und einer entsprechenden Seaside-Komponente nachprüfen, ob denn der alte oder der neue Stand aktiv ist. Ich weiss, auch das lässt sich automatisieren mit CURL und so weiter, aber es bringt eben immer noch mehr bewegliche Teile ins Puzzle…

      Naja, meine Umgehungslösung scheint zu funktionieren, und ich hoffe, man denkt bei Instantiations auch ein bisschen darüber nach. Seaside ist ja – zumindest in VAST – noch ziemlich jung.

      Joachim

Comments are closed.