Spawnable Framework – Scripting for everyone
Posted on September 2, 2010Nick Flink Lead Dev Tools Engineer
In tuenti we are constantly running scripts. We run them to change the configuration of the site, to update the code on the site and even to check out our current working branch. Having a good scripting framework is obviously important when it comes to automating manual tasks. The way that we have chosen to look at scripts is a bit different than other approaches, because our scripts are quite important to us. We set up our servers so that we can deploy scripts to nearly every server and run them in a consistent manner across all of them. How we have managed to do that is to put our scripts at the top-most layer (same as the traditional index file), as a slightly different entry point. So if index.php is the cgi entry point to our site, Spawn.php is the cli entry point to the same site but is generally used for different purposes.
Using scripts
Scripts are often used to migrate users using older modules of our site to use the new ones or to prime memcache as to not strain the db with every little change. Other times we want to write scripts that work well from the command line with arguments manual pages etc. Sometimes we want to run a script on another server, perhaps even as another user. Many times you want to just run some script every day to analyze a specific set of data.
Requirements
Our scripts need to be maintainable (with code using regular domain layer where possible), easy to use and write for developers and easily executed in parallel. To increase maintainability we’ve decided that all scripts will have one entry point. Taking this approach leads us to: php Spawn.php .
To make the lives of our developers easier, the system has to be equipped with bash completion and by default support execution in different threads. Spawn.php can only run jobs that implement the Spawnable interface. To run a job, Spawn.php simply calls a static function run() as shown below in the Spawnable interface.
interface Spawnable{ static >public function run(); public function startJob(); }
Script types
CommandLineArgJob
The command line arg job allows easy use with the bash command line. It has bash completion, man pages, and uses reflection to construct your CommandLineArgJob with the same names used in the constructor on the command line. For example the following code class would be constructed with $number=5 and then startJob would run and echo the sent number.
class MyJob extends CmdLineArgJob{ private $number; public function __construct($number){ $this->number = $number; } public function startJob(){ echo 'number = ' . $this->number; } }
me@aserver:~#php Spawn.php MyJob number=5
number = 5
Bash completion tells you all of the optional arguments and if you miss one that isn’t optional eg. $number, the framework will tell you that it was required.
ForkedJob

We use forked jobs mainly for migrations. Executed jobs are being controlled by a parent process which fetches arguments for each of them from the input the script has received. As long as there is data for the next job and it hasn’t exceed the maximum number of active jobs, it will fork new threads with new jobs assigned to them. Developers can optimize the number of used resources against the speed of execution by changing the allowed number of active forked threads.
QueuedJob

The final job type is the most complicated and flexible one. The system requires a queue client daemon (QCD from now on) to be running on a target machine specifically configured to run particular types of jobs. A page execution can then create a job and enqueue it into a named queue located on one of our queue servers. If there is a QCD waiting on the queue server socket when the job arrives at the queue server it will be routed to the server on which the QCD is running. This allows us to specifically configure servers to run different jobs. Jobs that are transfered to the QCD through a queue server must extend the QueuedJob, which has an additional method named enqueue() beyond the normal run() and startJob() methods. QueuedJobs are transfered as serialized php classes, so when QCD deserializes them, all of the member variables remain intact. Finally the QCD calls the startJob() method and the job is executed. This has an interesting effect where in the constructor is actually called on the frontend server and the startJob is called on the processing server.
An example QueuedJob could appear like this:
php Spawn.php ExampleJob a:1:{i:0;a:2:{i:0;s:10:”ExampleJob”;i:1;O:10:”ExampleJob”:2:{s:15:”^@ExampleJob^@id”;s:3:”156″;}}}
An advantage to the serialized packaging is the ability to send by email specific commands. For example if I were to find a bug in the above ExampleJob, I could even send by email the packaged command line to another developer who would simply copy paste the line into a command prompt to reproduce and start debugging.






November 14, 2010 at 9:50 pm
Hello,Tuenti
I recommend you, test Codeigniter and Yii.
I think they are very hardy.
Bye..