#197237 - 2009-12-30 04:47 PM
For Allen ;)
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
Since Allen is bored and my brain is starting to hurt, I'll post this as a challenge...
I have a Kix script that is running as a service on 160+ servers across the enterprise. This script runs in two modes: 1. called without args, it assumes the service role as a scheduler/dispatcher, running in an infinite loop and dispatching commands after specific time periods elapse. At the appointed time, the script calls a copy of itself via RUN, with an argument that identifies one of 8 specific tasks to perform. 2. called with an arg, it performs one of the specific duties of the service and exits. The result/status is checked by the service.
This method allows all of the code to reside in a single script, making maintenance easy. It also eliminates a common problem with schedule/dispatch logic where a sub-task can run long enough to prevent another subtask from running it its timeslot. The dispatcher invokes subtasks on 2-minute Odd, 2-minute Even, 6-minute, hourly on each of four 15-minute periods, and 4-hour periods. Subtasks take from sub-second to multi-hours to complete, and logic is present to prevent multiple copies of sub-tasks from running as well as terminate tasks that run more than 6 hours. (so far, ~90 minutes has been the longest-running sub-task, so this time-limit value may be reduced.)
So - here's what I'm struggling with.. I want the script to update itself.
The service runs constantly, but generally has little work to do overnight. It synchronizes a directory structure over the WAN from an upstream peer determined by DNS SRV records. It also consolidates individual local logs and then pushes the consolidation logs upstream, where they ultimately arrive at the master server.
The script can check its peer to determine if a newer version of the script is available. It can copy that script over itself while it is running. The child processes will immediately begin using the new copy. To avoid any compatability issues with the child process returning data to the parent, I want the scheduler to restart itself as soon as it overwrites the script file. If this were Unix, I'd simply Exec the new copy, replacing the current running process with the latest script.
Restarting the service would accomplish this, but I have two challenges. If there are child processes running, they will die.. so I need to perform the "update & restart" only when no sub processes are active. This is easy enough to detect. The one I'm struggling with is how to restart the service without some external dependency. RUN "XNET RESTART KixService" would work, but would place the dependency on the XNET.EXE command. The UDFs can start/stop the service, but once I stop, I can't restart. None of the UDFs seem to support a restart.
So - in a nutshell - I'm looking for some ideas on how I can restart an actively running script via the script itself, thus loading the newest version.
Thanks!
Glenn
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
#197248 - 2009-12-31 12:59 AM
Re: For Allen ;)
[Re: Allen]
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
Hey - ya snooze, ya looze!
I'm using SrvAny, as I documented in the Script Vault article "Running Kix as a Service". SrvAny.exe controls the process that is defined. WKix32.exe is the application to run, and the script (and any args) is the parameter. Stopping SrvAny (the "KixService" service) terminates WKix32, which stops the script itself. That's why I was feeling challenged about restarting the service / script.
I'm going to test Fake Allen's solution tonight - I'm pretty sure that stopping it will stop the child, which is actually the main script. If that works, I'm home-free for a self-updating service.
The service itself is fairly simple, but performs many different scheduled tasks. All times are relative to service start and not time of day, with two exceptions.- Midnight - the log file name is changed, effectively resulting in a daily log rotation, and any log more than 30 days old is removed.
- 5am - a system-wide health check is performed by the Master Cylind - er - server. All SRV records are retrieved from DNS, each subordinate server is queried to insure it is responding, logging, and has a current replication of the data folder.
- Every hour:
:15 after startup - If Primary, check for changes and set MODIFIED flag :30 after startup - Update the local product index :45 after startup - If Primary, validate the content, remove any unauthorised files :60 after startup - If not primary, determine if a Folder Sync is needed - Every 6 minutes - Perform log consolodation, forwarding logs upstream to master server
- Every 2 minutes - If Primary or Secondary, check for sync requests and queue up to 20 sync processes.
With this in place, we can make a change to the primary server in the HQ location, and within 2 hours, that change is replicated to 160+ remote sites. Any process performed at the remote site is logged, and the logs are pushed upstream and consolidated/sorted on the primary server, again within a few hours. The sync time depends on the hierarchy - secondry and slaves that use the primary as their peer sync up within no more than 2 hours, while servers that sync with a secondary server can take up to 3 hours to sync. For our application, this is very fast, since the design spec was complete propogation within 24 hours.
Every server is listed in DNS with a SRV record, which identifies the hostname, host IP, subnet served, Class (primary/secondary/slave) and service communication port. With a simple DNS query, I can identify every server that provides this service, the server that provides service to my subnet, and - if the script is running on a server, what class of service it should provide.
I'll let you know how the "dumb" process works out, but am still open to ideas.
Thanks
Glenn
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
#197262 - 2009-12-31 07:48 PM
Re: For Allen ;)
[Re: NTDOC]
|
Allen
KiX Supporter
Registered: 2003-04-19
Posts: 4549
Loc: USA
|
Here is what I was looking for... old thread but might have some value here: GuardDog() - http://www.kixtart.org/forums/ubbthreads...true#Post129944
I wish I had the the time to write the code I was thinking but I just don't have time right now. The gist was, you would fire up your service and it would run the script and another kix process using the guarddog monitoring it. If the script/pid ended, it would autorestart it.
Edited by Allen (2009-12-31 07:49 PM)
|
Top
|
|
|
|
#197433 - 2010-01-19 04:01 PM
Re: For Allen ;)
[Re: Richard H.]
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
OK - here's the basis of what I wound up doing:Break On
; Example code to run a script as a service with the following features:
; * Manager process insures that the dispatch process keeps running
; * Manager process is controlled as a service by SrvAny.exe
; * Manager is invoked when the script is called without a $PID argument
; * Dispatcher process invokes a Processor to run specific tasks
; * Dispatcher runs in an infinite loop, tracks the PID of the manager
; and terminates if the Manager process terminates.
; * Dispatcher checks for a new version of the script and copies/exits for
; a self-updating feature. The Manager then restarts using the new script
; * Processor has a $PID of 0 and $ARG defining the specific task to perform
; * Processor performs the task and exits
; * Processor is invoked via RUN to run independently, allowing many Procesor
; events to occur. The code can limit the number of similar Processor tasks
; by tracking the PIDs.
Dim $Rc
; Set desired program options - NoVarsInStrings is required
$Rc = SetOption('NoVarsInStrings', 'On')
$Rc = SetOption('Explicit', 'On')
; If $PID is not specified, run as a front-end process
If Not IsDeclared($PID)
Global $PID
'Service front-end started. PID=' @PID ?
; Loop forever until terminated by external triggger (Service stop)
; no application-specific initialization is required.
; run the script with a PID arg so it runs as the back-end dispatcher
; It will terminate itself if the front-end PID dies. It can also
; terminate due to finding a newer version - it will self-update and exit,
; and this front-end will restart it.
While 1
Shell '%COMSPEC% /c Kix32.exe ' + @SCRIPTNAME + ' $PID=' + @PID
Loop
EndIf
; perform application initialization common to the Dispatch service and all Processor sub-tasks
Dim $A ; random arg value
Dim $I ; counter
'common init' ?
SRnd(@TICKS)
; if $ARG is declared, then this is a PROCESSOR sub-task running independent of the service
; Run the specific task and exit.
If IsDeclared($ARG)
; Use a Select/Case to decide what to do
; for now, pretend we're doing something useful...
'Specific subtask: ' $ARG ?
Sleep 5
Exit 0
EndIf
; This is the service back-end, which performs additional init, overall management, and
; scheduled dispatching of sub-tasks. It monitors the front-end process PID and terminates
; if it dies. It also checks for a newer version, copies it over the current version and
; exits. The front-end service will restart this, using the new code.
'Starting service back-end. PID=' @PID ?
$I = 0
While 1 ; start of main service loop
; insure that our parent process is running - we die if it isn't
$Rc = WmiProcessList($PID)
If @ERROR 'Terminate' ? Exit 0 EndIf
; test for service code update
If Exist(@SCRIPTDIR + '\service.new')
If Exist(@SCRIPTDIR + '\service.old')
Del @SCRIPTDIR + '\service.old'
EndIf
Move @SCRIPTDIR + '\service.kix' @SCRIPTDIR + '\service.old'
Move @SCRIPTDIR + '\service.new' @SCRIPTDIR + '\service.kix'
'Updated!' ?
Exit 0
EndIf
; Identify who we are
; change this to "version 2" and save as "service.new" to test the auto-update feature
'Version 1' ?
Sleep 0.5 ; pretend we're doing serious work here...
$I = $I + 1
; trigger a sub-task - would normally be done on a timer of some sort, with different
; Processor events occurring at different times. For now, just provide a random character
; as the $ARG argument
If $I = 20 ; run a subtask every 10 secondss
$I = 0
$A = Int(Rnd(8)) + 1
Run '%COMSPEC% /c wKix32.exe ' + @SCRIPTNAME + ' $PID=' + 0 + ' $ARG="' + Chr(64 + $A) + '"'
EndIf
Loop ; end of main service loop This is just a simple example, but it does everything I need - one script, runs as the manager, dispatcher, and processor depending on how it is invoked. Stopping the service terminates the manager. The Dispatcher sees the manager PID disappear and it terminates. If the Dispatcher sees a code update, it copies the new file over the current file and exits, where the manager simply restarts it using the new code. Any child Processor tasks from that point will use the new code version.
Thanks for the feedback!
Glenn
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
Moderator: Jochen, Allen, Radimus, Glenn Barnas, ShaneEP, Ruud van Velsen, Arend_, Mart
|
0 registered
and 447 anonymous users online.
|
|
|