Page MenuHomePhabricator

No OneTemporary

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..57872d0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/vendor/
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..9c9fca6
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,18 @@
+{
+ "name": "spitfire/defer",
+ "description": "Allozs spitfire apps to defer tasks to be executed at a later point",
+ "type": "library",
+ "license": "MIT",
+ "autoload": {
+ "psr-4": {
+ "spitfire\\defer\\" : "./src/"
+ }
+ },
+ "authors": [
+ {
+ "name": "César de la Cal Bretschneider",
+ "email": "cesar@magic3w.com"
+ }
+ ],
+ "require": {}
+}
diff --git a/src/Async.php b/src/Async.php
new file mode 100644
index 0000000..dfdd18b
--- /dev/null
+++ b/src/Async.php
@@ -0,0 +1,22 @@
+<?php namespace spitfire\defer;
+
+class TaskFactory
+{
+
+ private $db;
+
+ public function __construct(\spitfire\storage\database\DB $db)
+ {
+ $this->db = $db;
+ }
+
+ public function defer($defer, $task, $ttl = 10) {
+ $copy = $this->db->table('spitfire\core\async\Async')->newRecord();
+ $copy->status = 'pending';
+ $copy->ttl = $ttl;
+ $copy->scheduled = $defer < 86400 * 365 * 50? time() + $defer : $defer; #It's been the timestamp's 50th aniversary this year
+ $copy->task = serialize($task);
+ $copy->store();
+ }
+
+}
diff --git a/src/AsyncDirector.php b/src/AsyncDirector.php
new file mode 100644
index 0000000..e0f942c
--- /dev/null
+++ b/src/AsyncDirector.php
@@ -0,0 +1,81 @@
+<?php namespace spitfire\defer;
+
+use spitfire\mvc\Director;
+use Throwable;
+use function console;
+use function db;
+
+class AsyncDirector extends Director
+{
+
+ private $db;
+
+ public function __construct(\spitfire\storage\database\DB $db)
+ {
+ $this->db = $db;
+ }
+
+ public function pending() {
+
+ }
+
+ public function process() {
+ $pending = $this->db->table('spitfire\core\async\Async')->get('status', 'pending')
+ ->where('scheduled', '<', time())
+ ->all();
+
+ foreach ($pending as $record) {
+ /*@var $task Task*/
+ $task = unserialize($record->task);
+
+ if ($record->ttl < 1) {
+ $record->status = 'aborted';
+ $record->store();
+ console()->error('Task was abandoned for too many failures')->ln();
+ continue;
+ }
+
+ $record->status = 'processing';
+ $record->started = time();
+ $record->store();
+
+ try {
+ $result = $task->body();
+ $record->result = $result->getPayload();
+ $record->status = 'success';
+ $record->store();
+ console()->success('Task processed successfully')->ln();
+ }
+ catch (FailureException$ex) {
+ $result = $task->handleFailure($ex);
+ $record->result = $result->getPayload();
+ $record->status = 'error';
+ $record->store();
+
+ $copy = $this->db->table('spitfire\core\async\Async')->newRecord();
+ $copy->status = 'pending';
+ $copy->ttl = $record->ttl - 1;
+ $copy->scheduled = $record->scheduled + 300;
+ $copy->task = $record->task;
+ $copy->supersedes = $record;
+ $copy->store();
+ console()->error('Task failed')->ln();
+ }
+ catch (Throwable$e) {
+ $record->result = $e->getCode() . $e->getMessage() . PHP_EOL . $e->getTraceAsString();
+ $record->status = 'error';
+ $record->store();
+
+ $copy = $this->db->table('spitfire\core\async\async')->newRecord();
+ $copy->status = 'pending';
+ $copy->ttl = $record->ttl - 1;
+ $copy->scheduled = $record->scheduled + 300;
+ $copy->task = $record->task;
+ $copy->supersedes = $record;
+ $copy->store();
+ console()->error('Task failed - Unknown reason')->ln();
+ }
+ }
+ }
+
+}
diff --git a/src/AsyncModel.php b/src/AsyncModel.php
new file mode 100644
index 0000000..1bda4f2
--- /dev/null
+++ b/src/AsyncModel.php
@@ -0,0 +1,26 @@
+<?php namespace spitfire\defer;
+
+use spitfire\Model;
+use spitfire\storage\database\Schema;
+
+class AsyncModel extends Model
+{
+
+ /**
+ *
+ * @param Schema $schema
+ * @return Schema
+ */
+ public function definitions(Schema $schema) {
+ $schema->task = new \TextField();
+ $schema->result = new \TextField();
+ $schema->status = new \EnumField('pending', 'processing', 'success', 'error', 'aborted');
+ $schema->ttl = new \IntegerField(true);
+ $schema->started = new \IntegerField(true);
+ $schema->scheduled = new \IntegerField(true);
+ $schema->supersedes = new \Reference(AsyncModel::class);
+
+ $schema->index($schema->status, $schema->scheduled);
+ }
+
+}
diff --git a/src/FailureException.php b/src/FailureException.php
new file mode 100644
index 0000000..c21750c
--- /dev/null
+++ b/src/FailureException.php
@@ -0,0 +1,21 @@
+<?php namespace spitfire\defer;
+
+use Exception;
+
+class FailureException extends Exception
+{
+
+ private $extended;
+
+ public function __construct(string $message = "", int $code = 0, string $extended = "") {
+ $this->extended = $extended;
+ parent::__construct($message, $code, null);
+ }
+
+
+ public function getExtended() {
+ return $this->extended;
+ }
+
+
+}
diff --git a/src/Result.php b/src/Result.php
new file mode 100644
index 0000000..37b5da2
--- /dev/null
+++ b/src/Result.php
@@ -0,0 +1,16 @@
+<?php namespace spitfire\defer;
+
+class Result
+{
+
+ private $payload;
+
+ public function __construct($payload) {
+ $this->payload = $payload;
+ }
+
+ public function getPayload() {
+ return $this->payload;
+ }
+
+}
diff --git a/src/Task.php b/src/Task.php
new file mode 100644
index 0000000..3f3db3b
--- /dev/null
+++ b/src/Task.php
@@ -0,0 +1,41 @@
+<?php namespace spitfire\defer;
+
+use Serializable;
+
+abstract class Task implements Serializable
+{
+
+ private $settings;
+
+ public function __construct($settings) {
+ $this->settings = $settings;
+ }
+
+ public function serialize() {
+ return serialize(['settings' => $this->settings]);
+ }
+
+ public function unserialize($serialized) {
+ $data = unserialize($serialized);
+ $this->settings = $data['settings'];
+ }
+
+ /**
+ *
+ * @throws FailureException
+ */
+ abstract function body() : Result;
+
+ /**
+ *
+ * @param FailureException $e
+ * @return Result
+ */
+ public function handleFailure(FailureException$e): Result {
+ return new Result(sprintf('%s %s%s%s', $e->getCode(), $e->getMessage(), PHP_EOL, $e->getExtended()));
+ }
+
+ public function getSettings() {
+ return $this->settings;
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Apr 15, 7:51 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5272
Default Alt Text
(6 KB)

Event Timeline