Exemplo n.º 1
0
void ControllerTask::handle_delete()
{
  // Check the request has a valid JSON body
  std::string body = _req.get_rx_body();
  rapidjson::Document doc;
  doc.Parse<0>(body.c_str());

  if (doc.HasParseError())
  {
    TRC_INFO("Failed to parse document as JSON");
    send_http_reply(HTTP_BAD_REQUEST);
    return;
  }

  // Now loop through the body, pulling out the IDs/replica numbers
  // The JSON body should have the format:
  //  {"IDs": [{"ID": 123, "ReplicaIndex": 0},
  //           {"ID": 456, "ReplicaIndex": 2},
  //          ...]
  // The replica_index is zero-indexed (so the primary replica has an
  // index of 0).
  try
  {
    JSON_ASSERT_CONTAINS(doc, JSON_IDS);
    JSON_ASSERT_ARRAY(doc[JSON_IDS]);
    const rapidjson::Value& ids_arr = doc[JSON_IDS];

    // The request is valid, so respond with a 202. Now loop through the
    // the body and update the replica trackers.
    send_http_reply(HTTP_ACCEPTED);

    for (rapidjson::Value::ConstValueIterator ids_it = ids_arr.Begin();
         ids_it != ids_arr.End();
         ++ids_it)
    {
      try
      {
        TimerID timer_id;
        int replica_index;
        JSON_GET_INT_64_MEMBER(*ids_it, JSON_ID, timer_id);
        JSON_GET_INT_MEMBER(*ids_it, JSON_REPLICA_INDEX, replica_index);

        // Update the timer's replica_tracker to show that the replicas
        // at level 'replica_index' and higher have been informed
        // about the timer. This will tombstone the timer if all
        // replicas have been informed.
        _cfg->_handler->update_replica_tracker_for_timer(timer_id,
                                                         replica_index);
      }
      catch (JsonFormatError err)
      {
        TRC_INFO("JSON entry was invalid (hit error at %s:%d)",
                  err._file, err._line);
      }
    }
  }
  catch (JsonFormatError err)
  {
    TRC_INFO("JSON body didn't contain the IDs array");
    send_http_reply(HTTP_BAD_REQUEST);
  }
}
Exemplo n.º 2
0
Timer* Timer::from_json_obj(TimerID id,
                            uint64_t replica_hash,
                            std::string& error,
                            bool& replicated,
                            rapidjson::Value& doc)
{
  Timer* timer = NULL;

  try
  {
    JSON_ASSERT_CONTAINS(doc, "timing");
    JSON_ASSERT_CONTAINS(doc, "callback");

    // Parse out the timing block
    rapidjson::Value& timing = doc["timing"];
    JSON_ASSERT_OBJECT(timing);

    JSON_ASSERT_CONTAINS(timing, "interval");
    rapidjson::Value& interval = timing["interval"];
    JSON_ASSERT_INT(interval);

    // Extract the repeat-for parameter, if it's absent, set it to the interval
    // instead.
    int repeat_for_int;
    if (timing.HasMember("repeat-for"))
    {
      JSON_GET_INT_MEMBER(timing, "repeat-for", repeat_for_int);
    }
    else
    {
      repeat_for_int = interval.GetInt();
    }

    if ((interval.GetInt() == 0) && (repeat_for_int != 0))
    {
      // If the interval time is 0 and the repeat_for_int isn't then reject the timer.
      error = "Can't have a zero interval time with a non-zero (%s) repeat-for time",
              std::to_string(repeat_for_int);
      return NULL;
    }

    timer = new Timer(id, (interval.GetInt() * 1000), (repeat_for_int * 1000));

    if (timing.HasMember("start-time-delta"))
    {
      // Timer JSON specified a time offset, use that to determine the true
      // start time.
      uint64_t start_time_delta;
      JSON_GET_INT_64_MEMBER(timing, "start-time-delta", start_time_delta);

      // This cast is safe as this sum is deliberately designed to wrap over UINT_MAX.
      timer->start_time_mono_ms = (uint32_t)(clock_gettime_ms(CLOCK_MONOTONIC) + start_time_delta);
    }
    else if (timing.HasMember("start-time"))
    {
      // Timer JSON specifies a start-time, use that instead of now.
      uint64_t real_start_time;
      JSON_GET_INT_64_MEMBER(timing, "start-time", real_start_time);
      uint64_t real_time = clock_gettime_ms(CLOCK_REALTIME);
      uint64_t mono_time = clock_gettime_ms(CLOCK_MONOTONIC);

      // This cast is safe as this sum is deliberately designed to wrap over UINT_MAX.
      timer->start_time_mono_ms = (uint32_t)(mono_time + real_start_time - real_time);
    }

    if (timing.HasMember("sequence-number"))
    {
      JSON_GET_INT_MEMBER(timing, "sequence-number", timer->sequence_number);
    }

    // Parse out the 'callback' block
    rapidjson::Value& callback = doc["callback"];
    JSON_ASSERT_OBJECT(callback);

    JSON_ASSERT_CONTAINS(callback, "http");
    rapidjson::Value& http = callback["http"];
    JSON_ASSERT_OBJECT(http);

    JSON_GET_STRING_MEMBER(http, "uri", timer->callback_url);
    JSON_GET_STRING_MEMBER(http, "opaque", timer->callback_body);

    if (doc.HasMember("reliability"))
    {
      // Parse out the 'reliability' block
      rapidjson::Value& reliability = doc["reliability"];
      JSON_ASSERT_OBJECT(reliability);

      if (reliability.HasMember("cluster-view-id"))
      {
        JSON_GET_STRING_MEMBER(reliability,
                               "cluster-view-id",
                               timer->cluster_view_id);
      }

      if (reliability.HasMember("replicas"))
      {
        rapidjson::Value& replicas = reliability["replicas"];
        JSON_ASSERT_ARRAY(replicas);

        if (replicas.Size() == 0)
        {
          error = "If replicas is specified it must be non-empty";
          delete timer; timer = NULL;
          return NULL;
        }

        timer->_replication_factor = replicas.Size();

        for (rapidjson::Value::ConstValueIterator it = replicas.Begin();
                                                  it != replicas.End();
                                                  ++it)
        {
          JSON_ASSERT_STRING(*it);
          timer->replicas.push_back(std::string(it->GetString(), it->GetStringLength()));
        }
      }
      else
      {
        if (reliability.HasMember("replication-factor"))
        {
          JSON_GET_INT_MEMBER(reliability,
                              "replication-factor",
                              timer->_replication_factor);
        }
        else
        {
          // Default replication factor is 2.
          timer->_replication_factor = 2;
        }
      }
    }
    else
    {
      // Default to 2 replicas
      timer->_replication_factor = 2;
    }

    timer->_replica_tracker = pow(2, timer->_replication_factor) - 1;

    if (timer->replicas.empty())
    {
      // Replicas not determined above, determine them now.  Note that this implies
      // the request is from a client, not another replica.
      replicated = false;
      timer->calculate_replicas(replica_hash);
    }
    else
    {
      // Replicas were specified in the request, must be a replication message
      // from another cluster node.
      replicated = true;
    }
  }
  catch (JsonFormatError err)
  {
    error = "Badly formed Timer entry - hit error on line " + std::to_string(err._line);
    delete timer; timer = NULL;
    return NULL;
  }

  return timer;
}