40 private const LEGACY_EVENTLOGGING_SCHEMA =
'MediaWikiPingback';
48 private const EVENT_PLATFORM_SCHEMA_ID =
'/analytics/legacy/mediawikipingback/1.0.0';
60 private const EVENT_PLATFORM_STREAM =
'eventlogging_MediaWikiPingback';
63 private const EVENT_PLATFORM_EVENT_INTAKE_SERVICE_URI =
64 'https://intake-analytics.wikimedia.org/v1/events?hasty=true';
96 string $eventIntakeUrl = self::EVENT_PLATFORM_EVENT_INTAKE_SERVICE_URI
104 $this->eventIntakeUri = $eventIntakeUrl;
105 $this->lockManager = $lockManager;
114 public function run(): void {
119 if ( $this->wasRecentlySent() ) {
123 if ( !$this->acquireLock() ) {
124 $this->logger->debug( __METHOD__ .
": couldn't acquire lock" );
129 if ( !$this->postPingback( $data ) ) {
130 $this->logger->warning( __METHOD__ .
": failed to send; check 'http' log channel" );
136 $dbw = $this->dbProvider->getPrimaryDatabase();
137 $timestamp = ConvertibleTimestamp::time();
138 $dbw->newInsertQueryBuilder()
139 ->insertInto(
'updatelog' )
140 ->row( [
'ul_key' => $this->key,
'ul_value' => $timestamp ] )
141 ->onDuplicateKeyUpdate()
142 ->uniqueIndexFields( [
'ul_key' ] )
143 ->set( [
'ul_value' => $timestamp ] )
144 ->caller( __METHOD__ )->execute();
145 $this->logger->debug( __METHOD__ .
": pingback sent OK ({$this->key})" );
151 private function wasRecentlySent(): bool {
152 $timestamp = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
153 ->select(
'ul_value' )
154 ->from(
'updatelog' )
155 ->where( [
'ul_key' => $this->key ] )
156 ->caller( __METHOD__ )->fetchField();
157 if ( $timestamp ===
false ) {
161 if ( ConvertibleTimestamp::time() - (
int)$timestamp > 60 * 60 * 24 * 30 ) {
175 private function acquireLock(): bool {
176 $cacheKey = $this->cache->makeKey(
'pingback', $this->key );
177 if ( !$this->cache->add( $cacheKey, 1, $this->cache::TTL_HOUR ) ) {
182 if ( !$this->lockManager->lockKey( $this->key ) ) {
200 $wiki = $this->fetchOrInsertId();
203 'event' => self::getSystemInfo( $this->config ),
204 'schema' => self::LEGACY_EVENTLOGGING_SCHEMA,
210 '$schema' => self::EVENT_PLATFORM_SCHEMA_ID,
211 'client_dt' => ConvertibleTimestamp::now( TS::ISO_8601 ),
217 'stream' => self::EVENT_PLATFORM_STREAM,
238 'PHP' => PHP_VERSION,
239 'OS' => PHP_OS .
' ' . php_uname(
'r' ),
240 'arch' => PHP_INT_SIZE === 8 ? 64 : 32,
241 'machine' => php_uname(
'm' ),
244 if ( isset( $_SERVER[
'SERVER_SOFTWARE'] ) ) {
245 $event[
'serverSoftware'] = $_SERVER[
'SERVER_SOFTWARE'];
248 $limit = ini_get(
'memory_limit' );
249 if ( $limit && $limit !==
"-1" ) {
250 $event[
'memoryLimit'] = $limit;
265 private function fetchOrInsertId(): string {
268 $id = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
269 ->select(
'ul_value' )
270 ->from(
'updatelog' )
271 ->where( [
'ul_key' =>
'PingBack' ] )
272 ->caller( __METHOD__ )->fetchField();
273 if ( $id !==
false ) {
277 $dbw = $this->dbProvider->getPrimaryDatabase();
278 $id = $dbw->newSelectQueryBuilder()
279 ->select(
'ul_value' )
280 ->from(
'updatelog' )
281 ->where( [
'ul_key' =>
'PingBack' ] )
282 ->caller( __METHOD__ )->fetchField();
283 if ( $id !==
false ) {
287 $id = MWCryptRand::generateHex( 32 );
288 $dbw->newInsertQueryBuilder()
289 ->insertInto(
'updatelog' )
290 ->row( [
'ul_key' =>
'PingBack',
'ul_value' => $id ] )
291 ->caller( __METHOD__ )->execute();
308 private function postPingback( array $data ): bool {
309 $request = $this->http->create( $this->eventIntakeUri, [
311 'postData' => FormatJson::encode( $data ),
313 $request->setHeader(
'Content-Type',
'application/json' );
317 return $result->isGood();