summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_dmclock_async_scheduler.cc
blob: 28738e9f3ab9199c6b79b22454592338534ba6fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab ft=cpp

#include "common/async/completion.h"
#include "rgw_dmclock_async_scheduler.h"
#include "rgw_dmclock_scheduler.h"

namespace rgw::dmclock {

AsyncScheduler::~AsyncScheduler()
{
  cancel();
  if (observer) {
    cct->_conf.remove_observer(this);
  }
}

const char** AsyncScheduler::get_tracked_conf_keys() const
{
  if (observer) {
    return observer->get_tracked_conf_keys();
  }
  static const char* keys[] = { "rgw_max_concurrent_requests", nullptr };
  return keys;
}

void AsyncScheduler::handle_conf_change(const ConfigProxy& conf,
                                        const std::set<std::string>& changed)
{
  if (observer) {
    observer->handle_conf_change(conf, changed);
  }
  if (changed.count("rgw_max_concurrent_requests")) {
    auto new_max = conf.get_val<int64_t>("rgw_max_concurrent_requests");
    max_requests = new_max > 0 ? new_max : std::numeric_limits<int64_t>::max();
  }
  queue.update_client_infos();
  schedule(crimson::dmclock::TimeZero);
}

int AsyncScheduler::schedule_request_impl(const client_id& client,
                                          const ReqParams& params,
                                          const Time& time, const Cost& cost,
                                          optional_yield yield_ctx)
{
    ceph_assert(yield_ctx);

    auto &yield = yield_ctx.get_yield_context();
    boost::system::error_code ec;
    async_request(client, params, time, cost, yield[ec]);

    if (ec){
      if (ec == boost::system::errc::resource_unavailable_try_again)
        return -EAGAIN;
      else
        return -ec.value();
    }

    return 0;
}

void AsyncScheduler::request_complete()
{
  --outstanding_requests;
  if(auto c = counters(client_id::count)){
    c->inc(throttle_counters::l_outstanding, -1);
  }
  schedule(crimson::dmclock::TimeZero);
}

void AsyncScheduler::cancel()
{
  ClientSums sums;

  queue.remove_by_req_filter([&] (RequestRef&& request) {
      inc(sums, request->client, request->cost);
      auto c = static_cast<Completion*>(request.release());
      Completion::dispatch(std::unique_ptr<Completion>{c},
                           boost::asio::error::operation_aborted,
                           PhaseType::priority);
      return true;
    });
  timer.cancel();

  for (size_t i = 0; i < client_count; i++) {
    if (auto c = counters(static_cast<client_id>(i))) {
      on_cancel(c, sums[i]);
    }
  }
}

void AsyncScheduler::cancel(const client_id& client)
{
  ClientSum sum;

  queue.remove_by_client(client, false, [&] (RequestRef&& request) {
      sum.count++;
      sum.cost += request->cost;
      auto c = static_cast<Completion*>(request.release());
      Completion::dispatch(std::unique_ptr<Completion>{c},
                           boost::asio::error::operation_aborted,
                           PhaseType::priority);
    });
  if (auto c = counters(client)) {
    on_cancel(c, sum);
  }
  schedule(crimson::dmclock::TimeZero);
}

void AsyncScheduler::schedule(const Time& time)
{
  timer.expires_at(Clock::from_double(time));
  timer.async_wait([this] (boost::system::error_code ec) {
      // process requests unless the wait was canceled. note that a canceled
      // wait may execute after this AsyncScheduler destructs
      if (ec != boost::asio::error::operation_aborted) {
        process(get_time());
      }
    });
}

void AsyncScheduler::process(const Time& now)
{
  // must run in the executor. we should only invoke completion handlers if the
  // executor is running
  assert(get_executor().running_in_this_thread());

  ClientSums rsums, psums;

  while (outstanding_requests < max_requests) {
    auto pull = queue.pull_request(now);

    if (pull.is_none()) {
      // no pending requests, cancel the timer
      timer.cancel();
      break;
    }
    if (pull.is_future()) {
      // update the timer based on the future time
      schedule(pull.getTime());
      break;
    }
    ++outstanding_requests;
    if(auto c = counters(client_id::count)){
      c->inc(throttle_counters::l_outstanding);
    }

    // complete the request
    auto& r = pull.get_retn();
    auto client = r.client;
    auto phase = r.phase;
    auto started = r.request->started;
    auto cost = r.request->cost;
    auto c = static_cast<Completion*>(r.request.release());
    Completion::post(std::unique_ptr<Completion>{c},
                     boost::system::error_code{}, phase);

    if (auto c = counters(client)) {
      auto lat = Clock::from_double(now) - Clock::from_double(started);
      if (phase == PhaseType::reservation) {
        inc(rsums, client, cost);
        c->tinc(queue_counters::l_res_latency, lat);
      } else {
        inc(psums, client, cost);
        c->tinc(queue_counters::l_prio_latency, lat);
      }
    }
  }

  if (outstanding_requests >= max_requests) {
    if(auto c = counters(client_id::count)){
      c->inc(throttle_counters::l_throttle);
    }
  }

  for (size_t i = 0; i < client_count; i++) {
    if (auto c = counters(static_cast<client_id>(i))) {
      on_process(c, rsums[i], psums[i]);
    }
  }
}

} // namespace rgw::dmclock