22
33namespace Devloops \Typesence ;
44
5+ use Devloops \Typesence \Lib \Node ;
56use GuzzleHttp \Exception \GuzzleException ;
67use Devloops \Typesence \Lib \Configuration ;
78use GuzzleHttp \Exception \ClientException ;
@@ -44,6 +45,11 @@ class ApiCall
4445 */
4546 private static $ nodes ;
4647
48+ /**
49+ * @var \Devloops\Typesence\Lib\Node
50+ */
51+ private static $ nearestNode ;
52+
4753 /**
4854 * @var int
4955 */
@@ -57,78 +63,79 @@ class ApiCall
5763 /**
5864 * ApiCall constructor.
5965 *
60- * @param \Devloops\Typesence\Lib\Configuration $config
66+ * @param \Devloops\Typesence\Lib\Configuration $config
6167 */
6268 public function __construct (Configuration $ config )
6369 {
6470 $ this ->config = $ config ;
6571 $ this ->client = new \GuzzleHttp \Client ();
6672 self ::$ nodes = $ this ->config ->getNodes ();
73+ self ::$ nearestNode = $ this ->config ->getNearestNode ();
6774 $ this ->nodeIndex = 0 ;
6875 $ this ->lastHealthCheckTs = time ();
76+ $ this ->initializeNodes ();
6977 }
7078
7179 /**
72- * @param string $endPoint
73- * @param array $params
74- * @param bool $asJson
80+ * Initialize Nodes
81+ */
82+ private function initializeNodes (): void
83+ {
84+ if (!empty (self ::$ nearestNode )) {
85+ $ this ->setNodeHealthcheck (self ::$ nearestNode , true );
86+ }
87+
88+ foreach (self ::$ nodes as &$ node ) {
89+ $ this ->setNodeHealthcheck ($ node , true );
90+ }
91+ }
92+
93+ /**
94+ * @param string $endPoint
95+ * @param array $params
96+ * @param bool $asJson
7597 *
7698 * @return string|array
7799 * @throws \Devloops\Typesence\Exceptions\TypesenseClientError
78100 * @throws \Exception
79101 */
80102 public function get (string $ endPoint , array $ params , bool $ asJson = true )
81103 {
82- return $ this ->makeRequest (
83- 'get ' ,
84- $ endPoint ,
85- $ asJson ,
86- [
87- 'data ' => $ params ?? [],
88- ]
89- );
104+ return $ this ->makeRequest ('get ' , $ endPoint , $ asJson , [
105+ 'data ' => $ params ?? [],
106+ ]);
90107 }
91108
92109 /**
93- * @param string $endPoint
94- * @param mixed $body
110+ * @param string $endPoint
111+ * @param mixed $body
95112 *
96113 * @return array
97114 * @throws \Devloops\Typesence\Exceptions\TypesenseClientError
98115 */
99116 public function post (string $ endPoint , $ body ): array
100117 {
101- return $ this ->makeRequest (
102- 'post ' ,
103- $ endPoint ,
104- true ,
105- [
106- 'data ' => $ body ?? [],
107- ]
108- );
118+ return $ this ->makeRequest ('post ' , $ endPoint , true , [
119+ 'data ' => $ body ?? [],
120+ ]);
109121 }
110122
111123 /**
112- * @param string $endPoint
113- * @param array $body
124+ * @param string $endPoint
125+ * @param array $body
114126 *
115127 * @return array
116128 * @throws \Devloops\Typesence\Exceptions\TypesenseClientError
117129 */
118130 public function put (string $ endPoint , array $ body ): array
119131 {
120- return $ this ->makeRequest (
121- 'put ' ,
122- $ endPoint ,
123- true ,
124- [
125- 'data ' => $ body ?? [],
126- ]
127- );
132+ return $ this ->makeRequest ('put ' , $ endPoint , true , [
133+ 'data ' => $ body ?? [],
134+ ]);
128135 }
129136
130137 /**
131- * @param string $endPoint
138+ * @param string $endPoint
132139 *
133140 * @return array
134141 * @throws \Devloops\Typesence\Exceptions\TypesenseClientError
@@ -141,10 +148,10 @@ public function delete(string $endPoint): array
141148 /**
142149 * Makes the actual http request, along with retries
143150 *
144- * @param string $method
145- * @param string $endPoint
146- * @param bool $asJson
147- * @param array $options
151+ * @param string $method
152+ * @param string $endPoint
153+ * @param bool $asJson
154+ * @param array $options
148155 *
149156 * @return string|array
150157 * @throws \Devloops\Typesence\Exceptions\TypesenseClientError
@@ -162,7 +169,7 @@ private function makeRequest(
162169 $ node ->setHealthy (false );
163170
164171 try {
165- $ url = $ node ->url () . $ endPoint ;
172+ $ url = $ node ->url (). $ endPoint ;
166173 $ reqOp = $ this ->getRequestOptions ();
167174 if (isset ($ options ['data ' ])) {
168175 if ($ method === 'get ' ) {
@@ -178,43 +185,41 @@ private function makeRequest(
178185
179186 $ statusCode = $ response ->getStatusCode ();
180187 if (0 < $ statusCode && $ statusCode < 500 ) {
181- $ node -> setHealthy ( true );
188+ $ this -> setNodeHealthcheck ( $ node , true );
182189 }
183190
184- if (($ method !== 'post ' && $ statusCode !== 200 )
185- || ($ method === 'post '
186- && !($ statusCode === 200
187- || $ statusCode === 201 ))) {
188- $ errorMessage = json_decode (
189- $ response ->getBody ()->getContents (),
190- true
191- )['message ' ] ?? 'API error. ' ;
192- throw $ this ->getException ($ statusCode )->setMessage (
193- $ errorMessage
194- );
191+ if (!(200 <= $ statusCode && $ statusCode < 300 )) {
192+ $ errorMessage =
193+ json_decode ($ response ->getBody ()->getContents (),
194+ true )['message ' ] ?? 'API error. ' ;
195+ throw $ this ->getException ($ statusCode )
196+ ->setMessage ($ errorMessage );
195197 }
196198
197- return $ asJson ? json_decode (
198- $ response ->getBody ()->getContents (),
199- true
200- ) : $ response ->getBody ()->getContents ();
199+ return $ asJson ? json_decode ($ response ->getBody ()
200+ ->getContents (),
201+ true ) : $ response ->getBody ()->getContents ();
201202 } catch (ClientException $ exception ) {
202203 if ($ exception ->getResponse ()->getStatusCode () === 408 ) {
203204 continue ;
204205 }
205- throw $ this ->getException (
206- $ exception ->getResponse ()->getStatusCode ()
207- )->setMessage ($ exception ->getMessage ());
206+ $ this ->setNodeHealthcheck ($ node , false );
207+ throw $ this ->getException ($ exception ->getResponse ()
208+ ->getStatusCode ())
209+ ->setMessage ($ exception ->getMessage ());
208210 } catch (RequestException $ exception ) {
209211 if ($ exception ->getResponse ()->getStatusCode () === 408 ) {
210212 continue ;
211213 }
212- throw $ this ->getException (
213- $ exception ->getResponse ()->getStatusCode ()
214- )->setMessage ($ exception ->getMessage ());
214+ $ this ->setNodeHealthcheck ($ node , false );
215+ throw $ this ->getException ($ exception ->getResponse ()
216+ ->getStatusCode ())
217+ ->setMessage ($ exception ->getMessage ());
215218 } catch (TypesenseClientError $ exception ) {
219+ $ this ->setNodeHealthcheck ($ node , false );
216220 throw $ exception ;
217221 } catch (\Exception $ exception ) {
222+ $ this ->setNodeHealthcheck ($ node , false );
218223 throw $ exception ;
219224 }
220225
@@ -230,20 +235,21 @@ private function makeRequest(
230235 private function getRequestOptions (): array
231236 {
232237 return [
233- 'headers ' => [
238+ 'headers ' => [
234239 self ::API_KEY_HEADER_NAME => $ this ->config ->getApiKey (),
235- ],
236- 'connect_timeout ' => $ this ->config ->getTimeoutSeconds (),
240+ ], 'connect_timeout ' => $ this ->config ->getConnectionTimeoutSeconds (),
237241 ];
238242 }
239243
240244 /**
245+ * @param \Devloops\Typesence\Lib\Node $node
246+ *
241247 * @return bool
242248 */
243- private function checkFailedNode ( ): bool
249+ private function nodeDueForHealthCheck ( Node $ node ): bool
244250 {
245251 $ currentTimestamp = time ();
246- $ checkNode = ($ currentTimestamp - $ this -> lastHealthCheckTs )
252+ $ checkNode = ($ currentTimestamp - $ node -> getLastAccessTs () )
247253 > self ::CHECK_FAILED_NODE_INTERVAL_S ;
248254 if ($ checkNode ) {
249255 $ this ->lastHealthCheckTs = $ currentTimestamp ;
@@ -252,6 +258,16 @@ private function checkFailedNode(): bool
252258 return $ checkNode ;
253259 }
254260
261+ /**
262+ * @param \Devloops\Typesence\Lib\Node $node
263+ * @param bool $isHealthy
264+ */
265+ public function setNodeHealthcheck (Node $ node , bool $ isHealthy ): void
266+ {
267+ $ node ->setHealthy ($ isHealthy );
268+ $ node ->setLastAccessTs (time ());
269+ }
270+
255271 /**
256272 * Returns a healthy host from the pool in a round-robin fashion
257273 * Might return an unhealthy host periodically to check for recovery.
@@ -260,12 +276,18 @@ private function checkFailedNode(): bool
260276 */
261277 public function getNode (): Lib \Node
262278 {
279+ if (self ::$ nearestNode ) {
280+ if (self ::$ nearestNode ->isHealthy ()
281+ || $ this ->nodeDueForHealthCheck (self ::$ nearestNode )) {
282+ return self ::$ nearestNode ;
283+ }
284+ }
263285 $ i = 0 ;
264286 while ($ i < count (self ::$ nodes )) {
265287 $ i ++;
266288 $ this ->nodeIndex = ($ this ->nodeIndex + 1 ) % count (self ::$ nodes );
267289 if (self ::$ nodes [$ this ->nodeIndex ]->isHealthy ()
268- || $ this ->checkFailedNode ( )) {
290+ || $ this ->nodeDueForHealthCheck ( self :: $ nodes [ $ this -> nodeIndex ] )) {
269291 return self ::$ nodes [$ this ->nodeIndex ];
270292 }
271293 }
@@ -279,7 +301,7 @@ public function getNode(): Lib\Node
279301 }
280302
281303 /**
282- * @param int $httpCode
304+ * @param int $httpCode
283305 *
284306 * @return \Devloops\Typesence\Exceptions\TypesenseClientError
285307 */
0 commit comments