PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.18.4
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.18.4
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 1 year ago Integrations 1 year ago StorageDriver 1 year ago Url 1 year ago Utils 1 year ago data 1 year ago Api.php 1 year ago Backlog.php 1 year ago BacklogReplayTimeoutException.php 1 year ago ChallengeProcessingException.php 1 year ago ChallengeVerificationException.php 1 year ago ConfigFetcherException.php 1 year ago Crypto.php 1 year ago Device.php 1 year ago DeviceType.php 1 year ago ElementRevision.php 1 year ago EmptyConfigException.php 1 year ago ExcludeEntry.php 1 year ago ExcludeOperationType.php 1 year ago FileHandle.php 1 year ago Filesystem.php 1 year ago HealthStatus.php 1 year ago IntegrationUrl.php 1 year ago NitroPack.php 11 months ago NoConfigException.php 1 year ago Pagecache.php 1 year ago PurgeType.php 1 year ago ServiceDownException.php 1 year ago StorageException.php 1 year ago VariationCookieException.php 1 year ago WebhookException.php 1 year ago Website.php 1 year 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