Skip to content

Adding Batch package - #414

Merged
tmatsuo merged 41 commits into
googleapis:masterfrom
tmatsuo:batch
Apr 18, 2017
Merged

Adding Batch package#414
tmatsuo merged 41 commits into
googleapis:masterfrom
tmatsuo:batch

Conversation

@tmatsuo

@tmatsuo tmatsuo commented Mar 25, 2017

Copy link
Copy Markdown
Contributor

Adding the Batch package for asynchronously execute some jobs in batch.

The basic design is described at: https://wp.gaeflex.ninja/2017/03/02/asyncbatchrunner-design-proposal/

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Mar 25, 2017
@jdpedrie

jdpedrie commented Mar 25, 2017

Copy link
Copy Markdown
Contributor

I wonder if the BatchDaemon could be run as part of the composer google-cloud CLI command we already have? It's currently used just for development stuff, but there's not really a reason we couldn't expand that.

@tmatsuo

tmatsuo commented Mar 25, 2017

Copy link
Copy Markdown
Contributor Author

@jdpedrie That's a good idea! Can we install vendor/bin/google-cloud when installed by composer?

@tmatsuo

tmatsuo commented Mar 25, 2017

Copy link
Copy Markdown
Contributor Author

@jdpedrie On the second thought, I think it's too much to include symfony/console as production dependency, just for the daemon. I would create an entry point script for the daemon and install it in vendor/bin.

@jdpedrie

Copy link
Copy Markdown
Contributor

Good point!

@tmatsuo tmatsuo changed the title [WIP] Adding Batch package Adding Batch package Mar 28, 2017
@tmatsuo
tmatsuo requested review from dwsupplee and jdpedrie March 28, 2017 18:21
@tmatsuo

tmatsuo commented Mar 29, 2017

Copy link
Copy Markdown
Contributor Author

@jdpedrie @dwsupplee I think it's ready for a real review. PTAL

Some examples for using this library.

BatchLogger (need to put it somewhere autoloadable)

<?php

namespace Google\Cloud\Core\Logger;

use Google\Cloud\Logging\LoggingClient;

/**                                                                                                                                                                
 * A logger that batches logs.                                                                                                                                     
 */
class BatchLogger
{
    /**                                                                                                                                                            
     * Send the given log entries.                                                                                                                                 
     *                                                                                                                                                             
     * @param array $items                                                                                                                                         
     */
    public function sendEntries($items)
    {                                                                                                                                           
        $c = new LoggingClient();
        $logger = $c->logger('my-log', [
            'resource' => [
                'type' => 'gcs_bucket',
                'labels' => [
                    'bucket_name' => 'my_bucket'
                ]
            ]
        ]);
        try {
            $logger->writeBatch($items);
        } catch (\Exception $e) {
            fwrite(STDERR, $e->getMessage() . PHP_EOL);
            return false;
        }
        return true;
    }
}

The code for registering the job:

use Google\Cloud\Core\Batch\BatchRunner;
use Google\Cloud\Core\Logger\BatchLogger;

if (count($argv) === 2) {
    $num = intval($argv[1]);
} else {
    $num = 1;
}

$batchLogger = new BatchLogger();

$r = new BatchRunner();
$r->registerjob(
    'logging',
    array($batchLogger, 'sendEntries'),
    array(
        'workerNum' => $num,
        'batchSize' => 1000
    )
);

The code for submitting the item:

use Google\Cloud\Core\Batch\BatchRunner;
use Google\Cloud\Logging\LoggingClient;

$r = new BatchRunner();
$c = new LoggingClient();
$logger = $c->logger('my-log', [
    'resource' => [
        'type' => 'gcs_bucket',
        'labels' => [
            'bucket_name' => 'my_bucket'
        ]
    ]
]);

if (count($argv) == 1) {
    $num = 10000;
} else {
    $num = intval($argv[1]);
}

//$message = str_repeat('x', 8192);
$message = 'log message';

$start = microtime(true);
for ($i = 0; $i < $num; $i++) {
    $r->submitItem('logging', $logger->entry($message));
}
$end = microtime(true);
printf('%d logs sent in %f seconds, at %f LPS' . PHP_EOL, $num, $end - $start, $num / ($end - $start));

For the logging client to work, you may need to set an envvar for auth:

export GOOGLE_APPLICATION_CREDENTIALS=/some/where/secret.json

In the first terminal, you can run the daemon:

bin/google-cloud-batch-daemon

Then export an envvar:

export IS_BATCH_DAEMON_RUNNING=true

and register the job and run the submit code. It can send ~20k logs/seconds with 1000 batch and 10 child processes :)

@tmatsuo

tmatsuo commented Apr 12, 2017

Copy link
Copy Markdown
Contributor Author

Thanks @jdpedrie for the review! Is there anything left?

@dwsupplee

Copy link
Copy Markdown
Contributor

I'd like to take a look as well, I have some time slated for tomorrow. Sorry for the hold up we've been a bit busy :).

@tmatsuo

tmatsuo commented Apr 16, 2017

Copy link
Copy Markdown
Contributor Author

FYI, the BatchLogger class (the client of this Batch package) is available at:
https://github.com/tmatsuo/google-cloud-php/tree/batch-logger

These libraries perform really really well and I'm so excited :)

@dwsupplee
I'd appreciate it if we could proceed on this PR.

@dwsupplee dwsupplee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fantastic addition, thank you @tmatsuo. 👍

Comment thread src/Core/Batch/BatchConfig.php Outdated
*/
public function getJobFromId($identifier)
{
return array_key_exists($identifier, $this->idmap) ?

This comment was marked as spam.

This comment was marked as spam.

Comment thread src/Core/Batch/BatchDaemon.php Outdated
/* @var BatchRunner */
private $runner;

/* @var boolean */

This comment was marked as spam.

This comment was marked as spam.

pcntl_signal(SIGINT, [$this, "sigHandler"]);
pcntl_signal(SIGHUP, [$this, "sigHandler"]);
pcntl_signal(SIGALRM, [$this, "sigHandler"]);
$this->command = sprintf('exec php -d auto_prepend_file="" %s', $entrypoint);

This comment was marked as spam.

This comment was marked as spam.

foreach ($v as $proc) {
$status = proc_get_status($proc);
// Keep sending SIGTERM until the child exits.
while ($status['running'] === true) {

This comment was marked as spam.

This comment was marked as spam.

Comment thread src/Core/bin/google-cloud-batch-daemon Outdated
if (count($argv) == 1) {
$daemon->runParent();
} else {
$idNum = intval($argv[1]);

This comment was marked as spam.

This comment was marked as spam.

* Save the given BatchConfig.
*
* @param BatchConfig $config A BatchConfig to save.
* @return bool

This comment was marked as spam.

This comment was marked as spam.

if (! shm_has_var($shmid, self::VAR_KEY)) {
$result = new BatchConfig();
} else {
$result = shm_get_var($shmid, self::VAR_KEY);

This comment was marked as spam.

This comment was marked as spam.

$item
);
if ($result === false) {
// Try to put the content in a temp file and send the filename.

This comment was marked as spam.

This comment was marked as spam.

Comment thread src/Core/Batch/BatchRunner.php Outdated
$job = $this->getJobFromId($identifier);
if ($job === null) {
throw new \RuntimeException(
'The identifier does not exist: ' . $identifier

This comment was marked as spam.

This comment was marked as spam.

*/
public function isDaemonRunning()
{
return getenv('IS_BATCH_DAEMON_RUNNING') !== false;

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

@tmatsuo

tmatsuo commented Apr 18, 2017

Copy link
Copy Markdown
Contributor Author

@dwsupplee Thanks for your review! PTAL

I had a really interesting observation with that change.

The last change that introduced shouldRunTheJob, actually slowed down
the Log sending speed by 8X.

If we stay in one single function, it can send 40k log lines in one
second, but with the new helper method, it can only send 5k log lines in
one second. This is really an interesting observation and definitely we
need to study more, but for now I'd like to keep the inline if
statement for the performance reason.
@tmatsuo

tmatsuo commented Apr 18, 2017

Copy link
Copy Markdown
Contributor Author

@dwsupplee

I needed to revert the change in BatchDaemon for performance reasons. Please see the commit comment for more detail.

@dwsupplee dwsupplee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :)

@tmatsuo
tmatsuo merged commit f96a4f3 into googleapis:master Apr 18, 2017
@tmatsuo
tmatsuo deleted the batch branch April 18, 2017 16:33
@tmatsuo
tmatsuo restored the batch branch April 24, 2017 22:38
tmatsuo pushed a commit that referenced this pull request Apr 24, 2017
tmatsuo pushed a commit to tmatsuo/google-cloud-php that referenced this pull request Apr 24, 2017
dwsupplee pushed a commit that referenced this pull request Apr 24, 2017
@tmatsuo
tmatsuo deleted the batch branch April 24, 2017 23:13
dwsupplee pushed a commit to googleapis/google-cloud-php-core that referenced this pull request Apr 25, 2017
@dwsupplee dwsupplee mentioned this pull request Jun 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes This human has signed the Contributor License Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants