PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.8.3
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.8.3
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / nitropack-sdk / NitroPack / SDK / Backlog.php
nitropack / nitropack-sdk / NitroPack / SDK Last commit date
Api 3 years ago Integrations 3 years ago StorageDriver 4 years ago Url 3 years ago data 6 years ago Api.php 3 years ago Backlog.php 4 years ago BacklogReplayTimeoutException.php 4 years ago ChallengeProcessingException.php 4 years ago ChallengeVerificationException.php 4 years ago ConfigFetcherException.php 4 years ago Crypto.php 3 years ago Device.php 6 years ago DeviceType.php 6 years ago ElementRevision.php 3 years ago EmptyConfigException.php 6 years ago FileHandle.php 5 years ago Filesystem.php 4 years ago HealthStatus.php 5 years ago IntegrationUrl.php 6 years ago NitroPack.php 3 years ago NoConfigException.php 5 years ago Pagecache.php 2 years ago PurgeType.php 3 years ago ServiceDownException.php 5 years ago StorageException.php 6 years ago VariationCookieException.php 5 years ago WebhookException.php 5 years ago Website.php 6 years ago
Backlog.php
241 lines
1 <?php
2
3 namespace NitroPack\SDK;
4 use NitroPack\SDK\Api\ResponseStatus;
5
6 class Backlog {
7 const TTL = 3600; // 1 hour in seconds
8
9 private $dataDir;
10 private $nitropack;
11 private $communicators;
12 private $queue;
13 private $queuePath;
14 private $isAcquired;
15 private $fileHandle;
16 private $header;
17 private $backlogFile = array('data', 'backlog.queue');
18
19 public function __construct($dataDir, $nitropack) {
20 $this->dataDir = $dataDir;
21 $this->nitropack = $nitropack;
22 $this->communicators = array();
23 $this->queue = array();
24 $this->queuePath = $this->getQueuePath();
25 $this->isAcquired = false;
26 $this->fileHandle = NULL;
27 $this->header = new \stdClass();
28 $this->header->offset = 0;
29 $this->header->firstProcessingTimestamp = 0;
30 $this->header->lastProcessingTimestamp = 0;
31 }
32
33 public function __destruct() {
34 $this->closeHandle();
35 }
36
37 public function delete() {
38 Filesystem::deleteFile($this->queuePath);
39 }
40
41 public function append($entry) {
42 if (defined("NITROPACK_DISABLE_BACKLOG")) return;
43 $fh = $this->getHandle();
44 Filesystem::flock($fh, LOCK_EX);
45 Filesystem::fseek($fh, 0, SEEK_END);
46 $this->writeEntry($fh, $this->encodeEntry($entry));
47 Filesystem::flock($fh, LOCK_UN);
48 }
49
50 public function replay($timeLimit = 10) {
51 if (defined("NITROPACK_DISABLE_BACKLOG")) return;
52 $fh = $this->getHandle();
53 Filesystem::flock($fh, LOCK_EX);
54 $lastProcessingTimestamp = $this->header->lastProcessingTimestamp;
55 if (time() - $lastProcessingTimestamp <= $timeLimit) {
56 return false;
57 }
58 $this->acquireBacklog($fh);
59 Filesystem::flock($fh, LOCK_UN);
60
61 $initialProcesssTime = $this->header->firstProcessingTimestamp;
62 if (time() - $initialProcesssTime > self::TTL) {
63 throw new BacklogReplayTimeoutException(sprintf("Backlog replay did not complete within %s seconds", self::TTL));
64 // In case there have been previous attempts at clearing the backlog and these attempts started more than the specified TTL seconds ago
65 // Perform a full purge and clear the backlog
66 }
67
68 if ($this->header->offset > 0) {
69 Filesystem::fseek($fh, $this->header->offset, SEEK_SET);
70 }
71 $start = microtime(true);
72 while (!$this->isEndOfQueue($fh) && NULL != ($entry = $this->getentry($fh)) && microtime(true) - $start < $timeLimit) {
73 $elapsedTime = microtime(true) - $start;
74 try {
75 $this->replayEntry($entry, $timeLimit - $elapsedTime);
76 } catch (\Exception $e) {
77 break;
78 }
79 }
80
81 if ($this->isEndOfQueue($fh)) {
82 $this->closeHandle();
83 $this->delete();
84 return true;
85 }
86
87 return false;
88 }
89
90 private function isEndOfQueue($fh) {
91 return Filesystem::feof($fh);
92 }
93
94 public function dumpEntries() {
95 $fh = $this->getHandle();
96 while (!$this->isEndOfQueue($fh) && NULL != ($entry = $this->getentry($fh))) {
97 try {
98 var_dump($entry);
99 } catch (\Exception $e) {
100 break;
101 }
102 }
103 }
104
105 public function dumpHeader() {
106 $this->getHandle();
107 var_dump($this->header);
108 }
109
110 public function resetOffset() {
111 $fh = $this->getHandle();
112 $this->header->offset = 0;
113 $this->writeHeader($fh);
114 }
115
116 public function exists() {
117 if (defined("NITROPACK_DISABLE_BACKLOG")) return false;
118 return Filesystem::fileExists($this->queuePath);
119 }
120
121 private function replayEntry($entry, $timeLimit) {
122 if (array_key_exists("siteSecret", $entry)) {
123 $requestMaker = $this->nitropack->getApi()->secure_request_maker;
124 } else {
125 $requestMaker = $this->nitropack->getApi()->request_maker;
126 }
127 $httpResponse = $requestMaker->makeRequest($entry["path"], $entry["headers"], $entry["cookies"], $entry["type"], $entry["bodyData"], false, $entry["verifySSL"]);
128 $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
129 $headers = $httpResponse->getHeaders();
130
131 $start = microtime(true);
132 while ($status == ResponseStatus::OK && !empty($headers["x-nitro-repeat"]) && microtime(true) - $start < $timeLimit) {
133 $httpResponse->replay(); // In reality $httpResponse is an instance of HttpClient which has the replay method
134 $status = ResponseStatus::getStatus($httpResponse->getStatusCode());
135 $headers = $httpResponse->getHeaders();
136 if ($status != ResponseStatus::OK) {
137 throw \RuntimeException("Unable to replay backlogged entry");
138 }
139 }
140
141 if ($status == ResponseStatus::OK && empty($headers["x-nitro-repeat"])) {
142 $fh = $this->getHandle();
143 $this->header->offset = Filesystem::ftell($fh);
144 $this->writeHeader($fh);
145 }
146 }
147
148 private function getQueuePath() {
149 $backlogFile = $this->backlogFile;
150 array_unshift($backlogFile, $this->dataDir);
151 return Filesystem::getOsPath($backlogFile);
152 }
153
154 private function getEntry($fh = NULL) {
155 $closeFile = empty($fh);
156 $fh = !empty($fh) ? $fh : $this->getHandle();
157 $entry = @Filesystem::fgets($fh);
158 if ($closeFile) {
159 Filesystem::fclose($fh);
160 }
161 return $this->decodeEntry($entry);
162 }
163
164 private function writeEntry($fh, $entry) {
165 Filesystem::fwrite($fh, $entry . "\n");
166 Filesystem::fflush($fh);
167 }
168
169 private function encodeEntry($entry) {
170 return base64_encode(json_encode($entry));
171 }
172
173 private function decodeEntry($entry) {
174 return json_decode(base64_decode($entry), true);
175 }
176
177 private function acquireBacklog($fh) {
178 $this->header->lastProcessingTimestamp = time();
179 if (!$this->header->firstProcessingTimestamp) {
180 $this->header->firstProcessingTimestamp = $this->header->lastProcessingTimestamp;
181 }
182 $this->writeHeader($fh);
183 $this->isAcquired = true;
184 }
185
186 private function releaseBacklog($fh) {
187 $this->header->lastProcessingTimestamp = 0;
188 $this->writeHeader($fh);
189 $this->isAcquired = false;
190 }
191
192 private function readHeader($fh) {
193 $offsetBackup = Filesystem::ftell($fh);
194 Filesystem::fseek($fh, 0, SEEK_SET);
195 $header = Filesystem::fread($fh, 12);
196 $parts = unpack("Loffset/LfirstProcessingTimestamp/LlastProcessingTimestamp", $header);
197 $this->header->offset = $parts["offset"];
198 $this->header->firstProcessingTimestamp = $parts["firstProcessingTimestamp"];
199 $this->header->lastProcessingTimestamp = $parts["lastProcessingTimestamp"];
200 Filesystem::fseek($fh, $offsetBackup, SEEK_SET);
201 }
202
203 private function writeHeader($fh) {
204 $offsetBackup = Filesystem::ftell($fh);
205 Filesystem::fseek($fh, 0, SEEK_SET);
206 Filesystem::fwrite($fh, pack("L*", $this->header->offset, $this->header->firstProcessingTimestamp, $this->header->lastProcessingTimestamp), 12);
207 Filesystem::fflush($fh);
208 Filesystem::fseek($fh, $offsetBackup, SEEK_SET);
209 }
210
211 private function getHandle() {
212 if ($this->fileHandle) return $this->fileHandle;
213
214 $fh = Filesystem::fopen($this->queuePath, "c+");
215 Filesystem::flock($fh, LOCK_EX);
216 Filesystem::fseek($fh, 0, SEEK_END);
217 $pos = Filesystem::ftell($fh);
218 if ($pos > 11) { // The first 12 bytes are reserved for the header. If the last pos is further than the 12th byte then the log is considered initialized
219 $this->readHeader($fh);
220 } else {
221 $this->writeHeader($fh);
222 }
223 Filesystem::fseek($fh, 12, SEEK_SET);
224 Filesystem::flock($fh, LOCK_UN);
225
226 $this->fileHandle = $fh;
227
228 return $this->fileHandle;
229 }
230
231 private function closeHandle() {
232 if ($this->fileHandle) {
233 if ($this->isAcquired) {
234 $this->releaseBacklog($this->fileHandle);
235 }
236 Filesystem::fclose($this->fileHandle);
237 $this->fileHandle = NULL;
238 }
239 }
240 }
241