Completed
Push — fix/race-condition-for-sync-st... ( 8e91b9...1e6f9b )
by
unknown
24:34 queued 13:05
created

Jetpack_Sync_Module_Full_Sync   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 197
Duplicated Lines 8.12 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 16
loc 197
rs 8.6
wmc 37
lcom 1
cbo 4

14 Methods

Rating   Name   Duplication   Size   Complexity  
A init_before_send() 0 4 1
A init_listeners() 0 5 1
A name() 0 3 1
C start() 0 48 7
A should_start_full_sync() 0 16 4
D update_sent_progress_action() 0 32 9
A set_status_queuing_started() 0 5 1
A get_status_queuing_started() 8 8 2
A set_status_queuing_finished() 0 4 1
A get_status_queuing_finished() 8 8 2
A is_started() 0 4 1
A is_finished() 0 4 1
B get_status() 0 26 4
A clear_status() 0 12 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * This class does a full resync of the database by
5
 * enqueuing an outbound action for every single object
6
 * that we care about.
7
 *
8
 * This class, and its related class Jetpack_Sync_Module, contain a few non-obvious optimisations that should be explained:
9
 * - we fire an action called jetpack_full_sync_start so that WPCOM can erase the contents of the cached database
10
 * - for each object type, we page through the object IDs and enqueue them by firing some monitored actions
11
 * - we load the full objects for those IDs in chunks of Jetpack_Sync_Module::ARRAY_CHUNK_SIZE (to reduce the number of MySQL calls)
12
 * - we fire a trigger for the entire array which the Jetpack_Sync_Listener then serializes and queues.
13
 */
14
15
require_once 'class.jetpack-sync-wp-replicastore.php';
16
17
class Jetpack_Sync_Module_Full_Sync extends Jetpack_Sync_Module {
18
	const STATUS_OPTION_PREFIX = 'jetpack_sync_full_';
19
	const FULL_SYNC_TIMEOUT = 3600;
20
21
	public function name() {
22
		return 'full-sync';
23
	}
24
25
	function init_listeners( $callable ) {
26
		// synthetic actions for full sync
27
		add_action( 'jetpack_full_sync_start', $callable );
28
		add_action( 'jetpack_full_sync_end', $callable );
29
	}
30
31
	function init_before_send() {
32
		// this is triggered after actions have been processed on the server
33
		add_action( 'jetpack_sync_processed_actions', array( $this, 'update_sent_progress_action' ) );
34
	}
35
36
	function start( $modules = null ) {
37
		if ( ! $this->should_start_full_sync() ) {
38
			return false;
39
		}
40
41
		// ensure listener is loaded so we can guarantee full sync actions are enqueued
42
		require_once dirname( __FILE__ ) . '/class.jetpack-sync-listener.php';
43
		Jetpack_Sync_Listener::get_instance();
44
45
		/**
46
		 * Fires when a full sync begins. This action is serialized
47
		 * and sent to the server so that it knows a full sync is coming.
48
		 *
49
		 * @since 4.2.0
50
		 */
51
		do_action( 'jetpack_full_sync_start' );
52
		$this->set_status_queuing_started();
53
54
		$prefix = self::STATUS_OPTION_PREFIX;
55
56
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
57
			$module_name = $module->name();
58
			if ( is_array( $modules ) && ! in_array( $module_name, $modules ) ) {
59
				continue;
60
			}
61
62
			$items_enqueued = $module->enqueue_full_sync_actions();
63
			if ( ! is_null( $items_enqueued ) && $items_enqueued > 0 ) {
64
				// TODO: only update this once every N items, then at end - why cause all that DB churn?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
65
				update_option( "{$prefix}_{$module->name()}_queued", $items_enqueued );
66
			}
67
		}
68
69
		$this->set_status_queuing_finished();
70
71
		$store = new Jetpack_Sync_WP_Replicastore();
72
73
		/**
74
		 * Fires when a full sync ends. This action is serialized
75
		 * and sent to the server with checksums so that we can confirm the
76
		 * sync was successful.
77
		 *
78
		 * @since 4.2.0
79
		 */
80
		do_action( 'jetpack_full_sync_end', $store->checksum_all() );
81
82
		return true;
83
	}
84
85
	private function should_start_full_sync() {
86
87
		// We should try sync if we haven't started it yet or if we have finished it.
88
		if ( ! $this->is_started() || $this->is_finished() ) {
89
			return true;
90
		}
91
92
		// allow enqueuing if last full sync was started more than FULL_SYNC_TIMEOUT seconds ago
93
		$prefix = self::STATUS_OPTION_PREFIX;
94
		$started_at = get_option( "{$prefix}_started", 0 );
95
		if ( intval( $started_at ) + self::FULL_SYNC_TIMEOUT < time() ) {
96
			return true;
97
		}
98
99
		return false;
100
	}
101
102
	function update_sent_progress_action( $actions ) {
103
		$prefix = self::STATUS_OPTION_PREFIX;
104
105
		// quick way to map to first items with an array of arrays
106
		$actions_with_counts = array_count_values( array_map( 'reset', $actions ) );
107
108
		if ( ! $this->is_started() || $this->is_finished() ) {
109
			return;
110
		}
111
112
		if ( isset( $actions_with_counts['jetpack_full_sync_start'] ) ) {
113
			update_option( "{$prefix}_sent_started", time() );
114
		}
115
116
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
117
			$module_actions = $module->get_full_sync_actions();
118
			$items_sent     = 0;
119
			foreach ( $module_actions as $module_action ) {
120
				if ( isset( $actions_with_counts[ $module_action ] ) ) {
121
					$items_sent += $actions_with_counts[ $module_action ];
122
				}
123
			}
124
125
			if ( $items_sent > 0 ) {
126
				update_option( "{$prefix}_{$module->name()}_sent", $items_sent );
127
			}	
128
		}
129
130
		if ( isset( $actions_with_counts['jetpack_full_sync_end'] ) ) {
131
			update_option( "{$prefix}_finished", time() );
132
		}
133
	}
134
135
	private function set_status_queuing_started() {
136
		$this->clear_status();
137
		$prefix = self::STATUS_OPTION_PREFIX;
138
		update_option( "{$prefix}_started", time() );
139
	}
140
141 View Code Duplication
	private function get_status_queuing_started() {
142
		$prefix = self::STATUS_OPTION_PREFIX;
143
		$status = get_option( "{$prefix}_started", null );
144
		if ( is_null( $status ) ) {
145
			return $status;
146
		}
147
		return intval( $status );
148
	}
149
150
	private function set_status_queuing_finished() {
151
		$prefix = self::STATUS_OPTION_PREFIX;
152
		update_option( "{$prefix}_queue_finished", time() );
153
	}
154
155 View Code Duplication
	private function get_status_queuing_finished() {
156
		$prefix = self::STATUS_OPTION_PREFIX;
157
		$status = get_option( "{$prefix}_queue_finished", null );
158
		if ( is_null( $status ) ) {
159
			return $status;
160
		}
161
		return intval( $status );
162
	}
163
164
	private function is_started() {
165
		$prefix = self::STATUS_OPTION_PREFIX;
166
		return ! is_null( get_option( "{$prefix}_started", null ) );
167
	}
168
169
	private function is_finished() {
170
		$prefix = self::STATUS_OPTION_PREFIX;
171
		return !! get_option( "{$prefix}_finished", null );
172
	}
173
174
	public function get_status() {
175
		$prefix = self::STATUS_OPTION_PREFIX;
176
		$status = array(
177
			'started'        => $this->get_status_queuing_started(),
178
			'queue_finished' => $this->get_status_queuing_finished(),
179
			'sent_started'   => get_option( "{$prefix}_sent_started", null ),
180
			'finished'       => get_option( "{$prefix}_finished", null ),
181
			'sent'           => array(),
182
			'queue'          => array(),
183
		);
184
185
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
186
			$queued = get_option( "{$prefix}_{$module->name()}_queued", null );
187
			$sent = get_option( "{$prefix}_{$module->name()}_sent", null );
188
189
			if ( ! is_null( $queued ) ) {
190
				$status[ 'queue' ][ $module->name() ] = $queued;
191
			}
192
			
193
			if ( ! is_null( $sent ) ) {
194
				$status[ 'sent' ][ $module->name() ] = $sent;
195
			}
196
		}
197
198
		return $status;
199
	}
200
201
	public function clear_status() {
202
		$prefix = self::STATUS_OPTION_PREFIX;
203
		delete_option( "{$prefix}_started" );
204
		delete_option( "{$prefix}_queue_finished" );
205
		delete_option( "{$prefix}_sent_started" );
206
		delete_option( "{$prefix}_finished" );
207
208
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
209
			delete_option( "{$prefix}_{$module->name()}_queued" );
210
			delete_option( "{$prefix}_{$module->name()}_sent" );
211
		}
212
	}
213
}
214