Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
PoolCounterClient
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 6
552
0.00% covered (danger)
0.00%
0 / 1
 setManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getConn
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 sendCommand
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
182
 acquireForMe
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 acquireForAnyone
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 release
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\PoolCounter;
22
23use MediaWiki\Status\Status;
24
25/**
26 * @since 1.16
27 */
28class PoolCounterClient extends PoolCounter {
29    /**
30     * @var ?resource the socket connection to the poolcounterd.  Closing this
31     * releases all locks acquired.
32     */
33    private $conn;
34
35    /**
36     * @var string The server host name
37     */
38    private $hostName;
39
40    /**
41     * @var PoolCounterConnectionManager
42     */
43    private $manager;
44
45    /**
46     * @param PoolCounterConnectionManager $manager
47     */
48    public function setManager( PoolCounterConnectionManager $manager ): void {
49        $this->manager = $manager;
50    }
51
52    /**
53     * @return Status
54     */
55    public function getConn() {
56        if ( !isset( $this->conn ) ) {
57            $status = $this->manager->get( $this->key );
58            if ( !$status->isOK() ) {
59                return $status;
60            }
61            // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
62            $this->conn = $status->value['conn'];
63            // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
64            $this->hostName = $status->value['hostName'];
65
66            // Set the read timeout to be 1.5 times the pool timeout.
67            // This allows the server to time out gracefully before we give up on it.
68            stream_set_timeout( $this->conn, 0, (int)( $this->timeout * 1e6 * 1.5 ) );
69        }
70        // TODO: Convert from Status to StatusValue
71        return Status::newGood( $this->conn );
72    }
73
74    /**
75     * @param string|int|float ...$args
76     * @return Status
77     */
78    public function sendCommand( ...$args ) {
79        $args = str_replace( ' ', '%20', $args );
80        $cmd = implode( ' ', $args );
81        $status = $this->getConn();
82        if ( !$status->isOK() ) {
83            return $status;
84        }
85        $conn = $status->value;
86        $this->logger->debug( "Sending pool counter command: $cmd" );
87        if ( fwrite( $conn, "$cmd\n" ) === false ) {
88            return Status::newFatal( 'poolcounter-write-error', $this->hostName );
89        }
90        $response = fgets( $conn );
91        if ( $response === false ) {
92            return Status::newFatal( 'poolcounter-read-error', $this->hostName );
93        }
94        $response = rtrim( $response, "\r\n" );
95        $this->logger->debug( "Got pool counter response: $response" );
96        $parts = explode( ' ', $response, 2 );
97        $responseType = $parts[0];
98        switch ( $responseType ) {
99            case 'LOCKED':
100                $this->onAcquire();
101                break;
102            case 'RELEASED':
103                $this->onRelease();
104                break;
105            case 'DONE':
106            case 'NOT_LOCKED':
107            case 'QUEUE_FULL':
108            case 'TIMEOUT':
109            case 'LOCK_HELD':
110                break;
111            case 'ERROR':
112            default:
113                $parts = explode( ' ', $parts[1], 2 );
114                $errorMsg = $parts[1] ?? '(no message given)';
115                return Status::newFatal( 'poolcounter-remote-error', $errorMsg, $this->hostName );
116        }
117        return Status::newGood( constant( "PoolCounter::$responseType" ) );
118    }
119
120    /**
121     * @param int|null $timeout
122     * @return Status
123     */
124    public function acquireForMe( $timeout = null ) {
125        $status = $this->precheckAcquire();
126        if ( !$status->isGood() ) {
127            return $status;
128        }
129        return $this->sendCommand( 'ACQ4ME', $this->key, $this->workers, $this->maxqueue,
130            $timeout ?? $this->timeout );
131    }
132
133    /**
134     * @param int|null $timeout
135     * @return Status
136     */
137    public function acquireForAnyone( $timeout = null ) {
138        $status = $this->precheckAcquire();
139        if ( !$status->isGood() ) {
140            return $status;
141        }
142        return $this->sendCommand( 'ACQ4ANY', $this->key, $this->workers, $this->maxqueue,
143            $timeout ?? $this->timeout );
144    }
145
146    /**
147     * @return Status
148     */
149    public function release() {
150        $status = $this->sendCommand( 'RELEASE' );
151
152        if ( $this->conn ) {
153            $this->manager->close( $this->conn );
154            $this->conn = null;
155        }
156
157        return $status;
158    }
159}