117
(unsigned long long)db_last_time_t,
118
(unsigned long long)now);
119
120
- RRDSET_FLAGS old = rrdset_flag_set_and_clear(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
121
- if(!(old & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS)) {
122
- if(rrdhost_sender_replicating_charts_plus_one(st->rrdhost) == 1)
123
- pulse_host_status(st->rrdhost, PULSE_HOST_STATUS_SND_REPLICATING, 0);
124
- }
120
+ // The receiver skips replication for obsolete charts (stream-receiver.c),
121
+ // so do not enter the replication bookkeeping here either: it would pin
122
+ // rrdhost_sender_replicating_charts and permanently gate the cleanup loop
123
+ // in svc_rrd_cleanup_obsolete_charts_from_all_hosts.
124
+ if(!rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)) {
125
+ // Claim before publish: increment the host counter BEFORE setting
126
+ // RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS, so any concurrent
127
+ // observer (obsoleter at rrdset.c, finalizer at
128
+ // stream-replication-sender.c, reset at stream-sender.c) that
129
+ // sees the flag set in its CAS old-value also sees a counter
130
+ // already incremented to match. Without this ordering, an
131
+ // observer can clear the flag and call rrdhost_sender_replicating
132
+ // _charts_minus_one() before the sender's increment, causing a
133
+ // transient underflow that other concurrent inc/dec can latch.
134
+ bool first_claim = (rrdhost_sender_replicating_charts_plus_one(st->rrdhost) == 1);
135
+
136
+ RRDSET_FLAGS old = rrdset_flag_set_and_clear(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS, RRDSET_FLAG_SENDER_REPLICATION_FINISHED);
137
+ bool we_caused_transition = !(old & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
138
+
139
+ if(we_caused_transition) {
140
+ if(first_claim)
141
+ pulse_host_status(st->rrdhost, PULSE_HOST_STATUS_SND_REPLICATING, 0);
142
+ }
143
+ else {
144
+ // Lost race: another sender already had IN_PROGRESS set, so
145
+ // our +1 is one too many. Roll it back; mirror the natural-
146
+ // finalize pulse-status flip on the 0 boundary.
147
+ if(rrdhost_sender_replicating_charts_minus_one(st->rrdhost) == 0)
148
+ pulse_host_status(st->rrdhost, PULSE_HOST_STATUS_SND_RUNNING, 0);
149
+ }
150
126
- replication_progress = true;
151
+ // Recheck after our CAS: a concurrent obsoleter may have set
152
+ // RRDSET_FLAG_OBSOLETE, OR a concurrent disconnect may have
153
+ // cleared the host's metadata-readiness flag. In either case the
154
+ // parent will not drive replication for this chart to completion
155
+ // and the natural decrement never fires; undo our state to keep
156
+ // the host counter and pulse status balanced. The atomic CAS
157
+ // ensures only the thread that observes IN_PROGRESS=1 actually
158
+ // decrements (handles the case where stream_sender_charts_and_
159
+ // replication_reset() already cleared the flag during a disconnect
160
+ // racing with this push).
161
+ if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) ||
162
+ !rrdhost_can_stream_metadata_to_parent(st->rrdhost))) {
163
+ if(we_caused_transition) {
164
+ RRDSET_FLAGS undo = rrdset_flag_set_and_clear(
165
+ st,
166
+ RRDSET_FLAG_SENDER_REPLICATION_FINISHED,
167
+ RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS);
168
+ if(undo & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS) {
169
+ if(rrdhost_sender_replicating_charts_minus_one(st->rrdhost) == 0)
170
+ pulse_host_status(st->rrdhost, PULSE_HOST_STATUS_SND_RUNNING, 0);
171
+ }
172
+ }
173
+ }
174
+ else {
175
+ replication_progress = true;
176
+ }
177
+ }
178
179
#ifdef NETDATA_LOG_REPLICATION_REQUESTS
180
internal_error(true, "REPLAY: 'host:%s/chart:%s' replication starts",