1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-09 10:00:57 +03:00
Files
mvfst/quic/api/Observer.h
Sridhar Srinivasan 3a4783713a Use separate flags to enable app data sent callbacks
Summary:
Previously we were using a single flag appDataSentEvents to enable 3 callbacks
(startWritingFromAppLimited, packetsWritten and appRateLimited). This commit
creates separate flags to enable each of these callbacks, we have use-cases
where Observers might want only packetsWritten events and not the others.

Also, this commit refactors the logic to deliver these 3 callbacks to all
observers into a separate method so they can be unit tested easily.

Reviewed By: bschlinker

Differential Revision: D27925011

fbshipit-source-id: 7f7436dfc3d50c3abcb8ec121b221d20e30b0c4b
2021-05-02 23:07:47 -07:00

426 lines
14 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#pragma once
#include <quic/QuicException.h>
#include <quic/common/SmallVec.h>
#include <quic/d6d/Types.h>
#include <quic/state/OutstandingPacket.h>
namespace folly {
class EventBase;
}
namespace quic {
class QuicSocket;
/**
* ===== Observer API =====
*/
/**
* Observer of socket events.
*/
class Observer {
public:
/**
* Observer configuration.
*
* Specifies events observer wants to receive.
*/
struct Config {
virtual ~Config() = default;
// following flags enable support for various callbacks.
// observer and socket lifecycle callbacks are always enabled.
bool evbEvents{false};
bool packetsWrittenEvents{false};
bool appRateLimitedEvents{false};
bool lossEvents{false};
bool spuriousLossEvents{false};
bool pmtuEvents{false};
bool rttSamples{false};
bool knobFrameEvents{false};
virtual void enableAllEvents() {
evbEvents = true;
packetsWrittenEvents = true;
appRateLimitedEvents = true;
rttSamples = true;
lossEvents = true;
spuriousLossEvents = true;
pmtuEvents = true;
knobFrameEvents = true;
}
/**
* Returns a config where all events are enabled.
*/
static Config getConfigAllEventsEnabled() {
Config config = {};
config.enableAllEvents();
return config;
}
};
/**
* Constructor for observer, uses default config (all callbacks disabled).
*/
Observer() : Observer(Config()) {}
/**
* Constructor for observer.
*
* @param config Config, defaults to auxilary instrumentaton disabled.
*/
explicit Observer(const Config& observerConfig)
: observerConfig_(observerConfig) {}
virtual ~Observer() = default;
/**
* Returns observers configuration.
*/
const Config& getConfig() {
return observerConfig_;
}
struct AppLimitedEvent {
AppLimitedEvent(
const std::deque<OutstandingPacket>& outstandingPackets,
const uint64_t writeCount)
: outstandingPackets(outstandingPackets), writeCount(writeCount) {}
// Cannot support copy ctors safely
AppLimitedEvent(const AppLimitedEvent&) = delete;
AppLimitedEvent(AppLimitedEvent&&) = delete;
// Reference to the current list of outstanding packets
const std::deque<OutstandingPacket>& outstandingPackets;
// The current write number for the write() call that
// caused this appLimitedEvent. Used by Observers to identify specific
// packets from the outstandingPacket list (above).
const uint64_t writeCount;
};
struct LostPacket {
explicit LostPacket(
bool lostbytimeout,
bool lostbyreorder,
const quic::OutstandingPacket& pkt)
: lostByTimeout(lostbytimeout),
lostByReorderThreshold(lostbyreorder),
packet(pkt) {}
bool lostByTimeout{false};
bool lostByReorderThreshold{false};
const quic::OutstandingPacket packet;
};
struct LossEvent {
explicit LossEvent(TimePoint time = Clock::now()) : lossTime(time) {}
bool hasPackets() {
return lostPackets.size() > 0;
}
void addLostPacket(
bool lostByTimeout,
bool lostByReorder,
const quic::OutstandingPacket& packet) {
lostPackets.emplace_back(lostByTimeout, lostByReorder, packet);
}
const TimePoint lossTime;
std::vector<LostPacket> lostPackets;
};
struct PacketRTT {
explicit PacketRTT(
TimePoint rcvTimeIn,
std::chrono::microseconds rttSampleIn,
std::chrono::microseconds ackDelayIn,
const quic::OutstandingPacket& pkt)
: rcvTime(rcvTimeIn),
rttSample(rttSampleIn),
ackDelay(ackDelayIn),
metadata(pkt.metadata),
lastAckedPacketInfo(pkt.lastAckedPacketInfo) {}
TimePoint rcvTime;
std::chrono::microseconds rttSample;
std::chrono::microseconds ackDelay;
const quic::OutstandingPacketMetadata metadata;
const folly::Optional<OutstandingPacket::LastAckedPacketInfo>
lastAckedPacketInfo;
};
struct PMTUBlackholeEvent {
explicit PMTUBlackholeEvent(
TimePoint blackholeTimeIn,
std::chrono::microseconds timeSinceLastNonSearchStateIn,
D6DMachineState lastNonSearchStateIn,
D6DMachineState currentStateIn,
uint64_t udpSendPacketLenIn,
uint64_t lastProbeSizeIn,
uint64_t blackholeDetectionWindowIn,
uint64_t blackholeDetectionThresholdIn,
const quic::OutstandingPacket& pkt)
: blackholeTime(blackholeTimeIn),
timeSinceLastNonSearchState(timeSinceLastNonSearchStateIn),
lastNonSearchState(lastNonSearchStateIn),
currentState(currentStateIn),
udpSendPacketLen(udpSendPacketLenIn),
lastProbeSize(lastProbeSizeIn),
blackholeDetectionWindow(blackholeDetectionWindowIn),
blackholeDetectionThreshold(blackholeDetectionThresholdIn),
triggeringPacketMetadata(pkt.metadata) {}
TimePoint blackholeTime;
// How long since last "stable" state
std::chrono::microseconds timeSinceLastNonSearchState;
D6DMachineState lastNonSearchState;
D6DMachineState currentState;
uint64_t udpSendPacketLen;
uint64_t lastProbeSize;
uint64_t blackholeDetectionWindow;
uint64_t blackholeDetectionThreshold;
// The metadata of the packet that triggerred blackhole signal
const quic::OutstandingPacketMetadata triggeringPacketMetadata;
};
struct PMTUUpperBoundEvent {
explicit PMTUUpperBoundEvent(
TimePoint upperBoundTimeIn,
std::chrono::microseconds timeSinceLastNonSearchStateIn,
D6DMachineState lastNonSearchStateIn,
uint64_t upperBoundPMTUIn,
uint64_t cumulativeProbesSentIn,
ProbeSizeRaiserType probeSizeRaiserTypeIn)
: upperBoundTime(upperBoundTimeIn),
timeSinceLastNonSearchState(timeSinceLastNonSearchStateIn),
lastNonSearchState(lastNonSearchStateIn),
upperBoundPMTU(upperBoundPMTUIn),
cumulativeProbesSent(cumulativeProbesSentIn),
probeSizeRaiserType(probeSizeRaiserTypeIn) {}
TimePoint upperBoundTime;
// How long it took to reach upperbound
std::chrono::microseconds timeSinceLastNonSearchState;
D6DMachineState lastNonSearchState;
uint64_t upperBoundPMTU;
uint64_t cumulativeProbesSent;
ProbeSizeRaiserType probeSizeRaiserType;
};
struct SpuriousLossEvent {
explicit SpuriousLossEvent(const TimePoint rcvTimeIn = Clock::now())
: rcvTime(rcvTimeIn) {}
bool hasPackets() {
return spuriousPackets.size() > 0;
}
void addSpuriousPacket(const quic::OutstandingPacket& pkt) {
spuriousPackets.emplace_back(pkt.lostByTimeout, pkt.lostByReorder, pkt);
}
const TimePoint rcvTime;
std::vector<LostPacket> spuriousPackets;
};
struct KnobFrameEvent {
explicit KnobFrameEvent(TimePoint rcvTimeIn, quic::KnobFrame knobFrame)
: rcvTime(rcvTimeIn), knobFrame(std::move(knobFrame)) {}
const TimePoint rcvTime;
const quic::KnobFrame knobFrame;
};
/**
* observerAttach() will be invoked when an observer is added.
*
* @param socket Socket where observer was installed.
*/
virtual void observerAttach(QuicSocket* /* socket */) noexcept {}
/**
* observerDetach() will be invoked if the observer is uninstalled prior
* to socket destruction.
*
* No further callbacks will be invoked after observerDetach().
*
* @param socket Socket where observer was uninstalled.
*/
virtual void observerDetach(QuicSocket* /* socket */) noexcept {}
/**
* destroy() will be invoked when the QuicSocket's destructor is invoked.
*
* No further callbacks will be invoked after destroy().
*
* @param socket Socket being destroyed.
*/
virtual void destroy(QuicSocket* /* socket */) noexcept {}
/**
* close() will be invoked when the socket is being closed.
*
* If the callback handler does not unsubscribe itself upon being called,
* then it may be called multiple times (e.g., by a call to close() by
* the application, and then again when closeNow() is called on
* destruction).
*
* @param socket Socket being closed.
* @param errorOpt Error information, if connection closed due to error.
*/
virtual void close(
QuicSocket* /* socket */,
const folly::Optional<
std::pair<QuicErrorCode, std::string>>& /* errorOpt */) noexcept {}
/**
* evbAttach() will be invoked when a new event base is attached to this
* socket. This will be called from the new event base's thread.
*
* @param socket Socket on which the new event base was attached.
* @param evb The new event base that is getting attached.
*/
virtual void evbAttach(
QuicSocket* /* socket */,
folly::EventBase* /* evb */) noexcept {}
/**
* evbDetach() will be invoked when an existing event base is detached
* from the socket. This will be called from the existing event base's thread.
*
* @param socket Socket on which the existing EVB is getting detached.
* @param evb The existing event base that is getting detached.
*/
virtual void evbDetach(
QuicSocket* /* socket */,
folly::EventBase* /* evb */) noexcept {}
/**
* startWritingFromAppLimited() is invoked when the socket is currenty
* app rate limited and is being asked to write a new set of Bytes.
. This callback is invoked BEFORE we write the
* new bytes to the socket, this is done so that Observers can collect
* metadata about the connection BEFORE the start of a potential Write Block.
*
* @param socket Socket that has potentially sent application data.
reference to the number of outstanding packets
BEFORE the call to write() socket data.
*/
virtual void startWritingFromAppLimited(
QuicSocket* /* socket */,
const AppLimitedEvent& /* appLimitedEvent */) {}
/**
* packetsWritten() is invoked when the socket writes retransmittable packets
* to the wire, those packets will be present in the outstanding packets list.
* Observers can use this callback (which will always be invoked AFTER
* startWritingFromAppLimited) to *update* the transport's metadata within the
* currently tracked WriteBlock.
*
* If an Observer receives startWritingFromAppLimited but doesn't receive
* packetsWritten (and instead directly receives appRateLimited), it means the
* socket witnessed non-app data writes - this scenario is irrelevant and
* should not be used to construct Write Blocks.
*
* @param socket Socket that has sent application data.
* @param appLimitedEvent The AppLimitedEvent details which contains a const
reference to the number of outstanding packets
*/
virtual void packetsWritten(
QuicSocket* /* socket */,
const AppLimitedEvent& /* appLimitedEvent */) {}
/**
* appRateLimited() is invoked when the socket is app rate limited.
*
* @param socket Socket that has become application rate limited.
* @param appLimitedEvent The AppLimitedEvent details which contains a const
reference to the number of outstanding packets
*/
virtual void appRateLimited(
QuicSocket* /* socket */,
const AppLimitedEvent& /* appLimitedEvent */) {}
/**
* packetLossDetected() is invoked when a packet loss is detected.
*
* @param socket Socket when the callback is processed.
* @param packet const reference to the packet that was determined to be
* lost.
*/
virtual void packetLossDetected(
QuicSocket*, /* socket */
const struct LossEvent& /* lossEvent */) {}
/**
* rttSampleGenerated() is invoked when a RTT sample is made.
*
* @param socket Socket when the callback is processed.
* @param packet const reference to the packet with the RTT.
*/
virtual void rttSampleGenerated(
QuicSocket*, /* socket */
const PacketRTT& /* RTT sample */) {}
/**
* pmtuProbingStarted() is invoked when server starts d6d.
*
* @param socket Socket when the callback is processed.
*/
virtual void pmtuProbingStarted(QuicSocket* /* socket */) {}
/**
* pmtuBlackholeDetected() is invoked when a PMTU blackhole is detected.
*
* @param pmtuBlackholeEvent const reference to the PMTU blackhole event
*/
virtual void pmtuBlackholeDetected(
QuicSocket*, /* socket */
const PMTUBlackholeEvent& /* pmtuBlackholeEvent */) {}
/**
* pmtuUpperBoundDetected() is invoked when a PMTU upperbound is detected.
*
* @param pmtuUpperBoundEvent const reference to the PMTU upperbound event
*/
virtual void pmtuUpperBoundDetected(
QuicSocket*, /* socket */
const PMTUUpperBoundEvent& /* pmtuUpperBoundEvent */) {}
/**
* spuriousLossDetected() is invoked when an ACK arrives for a packet that is
* declared lost
*
* @param socket Socket when the callback is processed.
* @param packet const reference to the lost packet.
*/
virtual void spuriousLossDetected(
QuicSocket*, /* socket */
const SpuriousLossEvent& /* lost packet */) {}
/**
* knobFrameReceived() is invoked when a knob frame is received.
*
* @param socket Socket when the callback is processed.
* @param event const reference to the KnobFrameEvent.
*/
virtual void knobFrameReceived(
QuicSocket*, /* socket */
const KnobFrameEvent& /* event */) {}
protected:
// observer configuration; cannot be changed post instantiation
const Config observerConfig_;
};
// Container for instrumentation observers.
// Avoids heap allocation for up to 2 observers being installed.
using ObserverVec = SmallVec<Observer*, 2>;
} // namespace quic