- All Implemented Interfaces:
TunnelGateway.QueuePreprocessor
- Direct Known Subclasses:
BatchedRouterPreprocessor
As explained in the tunnel document, the preprocessor has a lot of potential flexibility in delay, padding, or even reordering. We keep things relatively simple for now.
However much of the efficiency results from the clients selecting the correct MTU in the streaming lib such that the maximum-size streaming lib message fits in an integral number of tunnel messages. See ConnectionOptions in the streaming lib for details.
Aside from obvious goals of minimizing delay and padding, we also want to minimize the number of tunnel messages a message occupies, to minimize the impact of a router dropping a tunnel message. So there is some benefit in starting a message in a new tunnel message, especially if it will fit perfectly if we do that (a 964 or 1956 byte message, for example).
An idea for the future...
If we are in the middle of a tunnel msg and starting a new i2np msg, and this one won't fit, let's look to see if we have something that would fit instead by reordering: if (allocated > 0 && msg.getFragment == 0) { for (j = i+1, j < pending.size(); j++) { if it will fit and it is fragment 0 { msg = pending.remove(j) pending.add(0, msg) } } }.
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final RateStatMeanBatchFragment number of each flushed message, accumulated over the flush and reported as one mean sample.private final StringName shown in the logs for this preprocessor.private longHow long the current batch may still wait, in ms.private longWhen the first unsent message was queued, 0 if none.private final RateStatMeanBatchGateway lifetime of each flushed message, weight = its byte length.private static final booleanSwitch for the verbose batching logs.(package private) static longNot final or private so the test code can adjust.private static final intIf we have this many messages queued that are too small, flush them anyway Even small messages take up about 200 bytes or so.private static final intIf we have this much allocated, flush anyway.private static final intFull preprocessed message size in bytes (1003).(package private) static final longMinimum delay even under full pressure (ms) -- avoids busy-wait retry storms.Fields inherited from class TrivialPreprocessor
_context, _dataCache, _log, IV_SIZE, PREPROCESSED_SIZEModifier and TypeFieldDescriptionprotected final RouterContextThe router context.protected static final ByteCacheHere in tunnels, we take from the cache but never add to it.protected final LogThe logger.protected static final intLength of the IV.static final intSize of preprocessed data blocks.Constructor Summary
ConstructorsConstructorDescriptionBatchedPreprocessor(RouterContext ctx, String name) Create a new preprocessor for batched tunnel messages.Method Summary
Modifier and TypeMethodDescriptionprivate longcomputePendingDelay(long allocated, int pendingSize) Scale the flush delay inversely with buffer pressure so that near-full buffers flush almost immediately (low latency) while near-empty buffers wait longer (better aggregation).private voiddisplay(long allocated, List<PendingGatewayMessage> pending, String title) Logs the state of the queue, only if Log.INFO.longHow long to wait before flushing a queued message.private longgetDelayAmount(boolean shouldStat) Chooses how long the current batch may wait.protected longWait up to this long before sending (flushing) a small tunnel message Warning - overridden inBatchedRouterPreprocessor.private booleanpreprocessAndSend(List<PendingGatewayMessage> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) Body ofpreprocessQueue(List, TunnelGateway.Sender, TunnelGateway.Receiver), split out so the per-flush stat batches are flushed on every return path.booleanpreprocessQueue(List<PendingGatewayMessage> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) Return true if there were messages remaining, and we should queue up a delayed flush to clear themprotected voidsend(List<PendingGatewayMessage> pending, int startAt, int sendThrough, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) Preprocess the messages from the pending list, grouping items startAt through sendThrough (though only part of the last one may be fully sent), delivering them through the sender/receiver.private intwriteFragments(List<PendingGatewayMessage> pending, int startAt, int sendThrough, byte[] target, int offset) Write the fragments out of the pending list onto the target, updating each of the Pending message's offsets accordingly.Methods inherited from class TrivialPreprocessor
getInstructionAugmentationSize, getInstructionsSize, notePreprocessing, preprocess, writeFirstFragment, writeSubsequentFragmentModifier and TypeMethodDescriptionprotected static intgetInstructionAugmentationSize(PendingGatewayMessage msg, int offset, int instructionsSize) Get the augmentation size for instructions if fragmentation is needed.protected static intCalculate the size of the instructions for a pending message.protected voidnotePreprocessing(long messageId, int numFragments, int totalLength, List<Long> messageIds, String msg) Note preprocessing statistics.protected voidpreprocess(byte[] fragments, int fragmentLength) Wrap the preprocessed fragments with the necessary padding / checksums to act as a tunnel message.protected intwriteFirstFragment(PendingGatewayMessage msg, byte[] target, int offset) Write the first fragment of a tunnel message.protected intwriteSubsequentFragment(PendingGatewayMessage msg, byte[] target, int offset) Write a subsequent fragment of a tunnel message.
Field Details
_pendingSince
private long _pendingSinceWhen the first unsent message was queued, 0 if none._pendingDelay
private long _pendingDelayHow long the current batch may still wait, in ms._name
Name shown in the logs for this preprocessor._fragmentationBatch
Fragment number of each flushed message, accumulated over the flush and reported as one mean sample. Both stats here were sampled per fragment, which is two stat lookups and two monitors per rate period for every fragment leaving the gateway. Both are means, so one sample per flush reports the same quantity._writeDelayBatch
Gateway lifetime of each flushed message, weight = its byte length.DEBUG
private static final boolean DEBUGSwitch for the verbose batching logs.- See Also:
MIN_FLUSH_DELAY
static final long MIN_FLUSH_DELAYMinimum delay even under full pressure (ms) -- avoids busy-wait retry storms.- See Also:
FULL_SIZE
private static final int FULL_SIZEFull preprocessed message size in bytes (1003).- See Also:
DEFAULT_DELAY
static long DEFAULT_DELAYNot final or private so the test code can adjust.FORCE_BATCH_FLUSH
private static final int FORCE_BATCH_FLUSHIf we have this many messages queued that are too small, flush them anyway Even small messages take up about 200 bytes or so.- See Also:
FULL_ENOUGH_SIZE
private static final int FULL_ENOUGH_SIZEIf we have this much allocated, flush anyway. Tune this to trade off padding vs. fragmentation. The lower the value, the more we are willing to send off a tunnel msg that isn't full so the next message can start in a new tunnel msg to minimize fragmentation.This should be at most FULL_SIZE - (39 + a few), since you want to at least fit in the instructions and a few bytes.
- See Also:
Constructor Details
BatchedPreprocessor
Create a new preprocessor for batched tunnel messages.- Parameters:
ctx- the router contextname- the preprocessor name
Method Details
getSendDelay
protected long getSendDelay()Wait up to this long before sending (flushing) a small tunnel message Warning - overridden inBatchedRouterPreprocessor.- Returns:
- the send delay in ms
getDelayAmount
public long getDelayAmount()How long to wait before flushing a queued message.- Specified by:
getDelayAmountin interfaceTunnelGateway.QueuePreprocessor- Overrides:
getDelayAmountin classTrivialPreprocessor- Returns:
- delay in milliseconds
getDelayAmount
private long getDelayAmount(boolean shouldStat) Chooses how long the current batch may wait.- Parameters:
shouldStat- true to record the delay in the statistics- Returns:
- the remaining delay, in ms
computePendingDelay
private long computePendingDelay(long allocated, int pendingSize) Scale the flush delay inversely with buffer pressure so that near-full buffers flush almost immediately (low latency) while near-empty buffers wait longer (better aggregation).Pressure is the max of buffer fullness (allocated/FULL_SIZE) and queue pressure (pending.size/FORCE_BATCH_FLUSH), bounded [0,1].
delay = MIN_FLUSH_DELAY + (
getSendDelay()- MIN_FLUSH_DELAY) * (1 - pressure).- Parameters:
allocated- bytes allocated in the current tunnel messagependingSize- number of messages waiting to be batched- Returns:
- the flush delay in ms, from MIN_FLUSH_DELAY up to the configured send delay, lower the higher the buffer pressure
preprocessQueue
public boolean preprocessQueue(List<PendingGatewayMessage> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) Description copied from class:TrivialPreprocessorReturn true if there were messages remaining, and we should queue up a delayed flush to clear themNOTE: Unused here, see BatchedPreprocessor override, super is not called.
- Specified by:
preprocessQueuein interfaceTunnelGateway.QueuePreprocessor- Overrides:
preprocessQueuein classTrivialPreprocessor- Parameters:
pending- list of Pending objects for messages either unsent or partly sent. This list should be update with any values removed (the preprocessor owns the lock) Messages are not removed from the list until actually sent. The status of unsent and partially-sent messages is stored in the Pending structure.sender- the sender to hand the encrypted data torec- the receiver to accept encrypted data- Returns:
- true if we should delay before preprocessing again
preprocessAndSend
private boolean preprocessAndSend(List<PendingGatewayMessage> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) Body ofpreprocessQueue(List, TunnelGateway.Sender, TunnelGateway.Receiver), split out so the per-flush stat batches are flushed on every return path.- Parameters:
pending- the list of pending messagessender- the gateway callback that encrypts and forwards the preprocessed datarec- the receiver for preprocessed data- Returns:
- true if messages remain queued for a later flush
display
Logs the state of the queue, only if Log.INFO. Format: title: allocated: X pending: X (delay: X) [0]:offset/length/lifetime [1]:etc.- Parameters:
allocated- bytes already allocated to the batchpending- the messages still queuedtitle- a label for what triggered the display
send
protected void send(List<PendingGatewayMessage> pending, int startAt, int sendThrough, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) Preprocess the messages from the pending list, grouping items startAt through sendThrough (though only part of the last one may be fully sent), delivering them through the sender/receiver.- Parameters:
pending- the list of pending messagesstartAt- first index in pending to send (inclusive)sendThrough- last index in pending to send (inclusive)sender- the gateway callback that encrypts and forwards the preprocessed datarec- the receiver for preprocessed data
writeFragments
private int writeFragments(List<PendingGatewayMessage> pending, int startAt, int sendThrough, byte[] target, int offset) Write the fragments out of the pending list onto the target, updating each of the Pending message's offsets accordingly.- Parameters:
pending- the list of pending messagesstartAt- first index in pending to write (inclusive)sendThrough- last index in pending to write (inclusive)target- the preprocessed buffer to write the fragments intooffset- where in target to start writing- Returns:
- new offset into the target for further bytes to be written