Adding Batch package - #414
Conversation
|
I wonder if the BatchDaemon could be run as part of the |
|
@jdpedrie That's a good idea! Can we install |
|
@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 |
|
Good point! |
Only create the failure file with the in-memory implementation.
Added a script for retry. Added a dev script for clearing all the IPC data.
|
@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: In the first terminal, you can run the daemon: Then export an envvar: and register the job and run the submit code. It can send ~20k logs/seconds with 1000 batch and 10 child processes :) |
|
Thanks @jdpedrie for the review! Is there anything left? |
|
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 :). |
|
FYI, the These libraries perform really really well and I'm so excited :) @dwsupplee |
| */ | ||
| public function getJobFromId($identifier) | ||
| { | ||
| return array_key_exists($identifier, $this->idmap) ? |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| /* @var BatchRunner */ | ||
| private $runner; | ||
|
|
||
| /* @var boolean */ |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| 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.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| 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.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| if (count($argv) == 1) { | ||
| $daemon->runParent(); | ||
| } else { | ||
| $idNum = intval($argv[1]); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| * 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.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| 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.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| $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.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| $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.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| */ | ||
| public function isDaemonRunning() | ||
| { | ||
| return getenv('IS_BATCH_DAEMON_RUNNING') !== false; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
|
@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.
|
I needed to revert the change in |
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/