struct server *srv;
int err;
- balance_again:
if (s->pend_pos)
return SRV_STATUS_INTERNAL;
* ended and no new one will be able to dequeue that one.
* This is more visible with maxconn 1 where it can
* happen 1/1000 times, though the vast majority are
- * correctly recovered from. Since it's so rare and we
- * have no server assigned, the best solution in this
- * case is to detect the condition, dequeue our request
- * and balance it again.
+ * correctly recovered from.
+ * To work around that, when a server is getting idle,
+ * it will set the ready_srv field of the proxy.
+ * Here, if ready_srv is non-NULL, we get that server,
+ * and we attempt to switch its served from 0 to 1.
+ * If it works, then we can just run, otherwise,
+ * it means another stream will be running, and will
+ * dequeue us eventually, so we can just do nothing.
*/
- if (unlikely(pendconn_must_try_again(p)))
- goto balance_again;
+ if (unlikely(s->be->ready_srv != NULL)) {
+ struct server *newserv;
+
+ newserv = HA_ATOMIC_XCHG(&s->be->ready_srv, NULL);
+ if (newserv != NULL) {
+ int got_slot = 0;
+
+ while (_HA_ATOMIC_LOAD(&newserv->served) == 0) {
+ int served = 0;
+
+ if (_HA_ATOMIC_CAS(&newserv->served, &served, 1)) {
+ got_slot = 1;
+ break;
+ }
+ }
+ if (!got_slot) {
+ /*
+ * Somebody else can now
+ * wake up us, stop now.
+ */
+ return SRV_STATUS_QUEUED;
+ }
+
+ HA_SPIN_LOCK(QUEUE_LOCK, &p->queue->lock);
+ if (!p->node.node.leaf_p) {
+ /*
+ * Okay we've been queued and
+ * unqueued already, just leave
+ */
+ _HA_ATOMIC_DEC(&newserv->served);
+ return SRV_STATUS_QUEUED;
+ }
+ eb32_delete(&p->node);
+ HA_SPIN_UNLOCK(QUEUE_LOCK, &p->queue->lock);
+
+ _HA_ATOMIC_DEC(&p->queue->length);
+ _HA_ATOMIC_INC(&p->queue->idx);
+ _HA_ATOMIC_DEC(&s->be->totpend);
+
+ pool_free(pool_head_pendconn, p);
+
+ s->flags |= SF_ASSIGNED;
+ s->target = &newserv->obj_type;
+ s->pend_pos = NULL;
+ sess_change_server(s, newserv);
+ return SRV_STATUS_OK;
+ }
+ }
return SRV_STATUS_QUEUED;
}
if (p->lbprm.server_take_conn)
p->lbprm.server_take_conn(s);
}
+ if (s->served == 0 && p->served == 0 && !HA_ATOMIC_LOAD(&p->ready_srv)) {
+ /*
+ * If there is no task running on the server, and the proxy,
+ * let it known that we are ready, there is a small race
+ * condition if a task was being added just before we checked
+ * the proxy queue. It will look for that server, and use it
+ * if nothing is currently running, as there would be nobody
+ * to wake it up.
+ */
+ _HA_ATOMIC_STORE(&p->ready_srv, s);
+ /*
+ * Maybe a stream was added to the queue just after we
+ * checked, but before we set ready_srv so it would not see it,
+ * just in case try to run one more stream.
+ */
+ if (pendconn_process_next_strm(s, p, px_ok))
+ done++;
+ }
return done;
}
s->counters.down_trans++;
_srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_DOWN, cb_data.common, s);
}
+
+ /*
+ * If the server is no longer running, let's not pretend
+ * it can handle requests.
+ */
+ if (s->cur_state != SRV_ST_RUNNING && s->proxy->ready_srv == s)
+ HA_ATOMIC_STORE(&s->proxy->ready_srv, NULL);
+
s->counters.last_change = ns_to_sec(now_ns);
/* publish the state change */