Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distributor overview docs #3371

Merged
merged 7 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/sources/architecture/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ and to ensure that it is within the configured tenant (or global) limits. Valid
chunks are then split into batches and sent to multiple [ingesters](#ingester)
in parallel.

For more information, see the [Distributor](./distributor) page.

#### Hashing

Distributors use consistent hashing in conjunction with a configurable
Expand Down
41 changes: 41 additions & 0 deletions docs/sources/architecture/distributor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: Distributor
weight: 1000
---
# Distributor Component

This document builds upon the information in the [Loki Architecture](./) page.

## Where does it live?

The distributor is the first component on Loki's write path. It's responsible for validating, preprocessing, and applying a subset of rate limiting to incoming data before sending it to the ingester component.
owen-d marked this conversation as resolved.
Show resolved Hide resolved

## What does it do?

### Validation

The first step the distributor takes is to ensure that all incoming data is according to specification. This includes things like checking that the labels are valid Prometheus labels as well as ensuring the timestamps aren't too old or too new or the log lines aren't too long.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well as ensuring the timestamps aren't too old or too new or the log lines aren't too long

Again curious about the timestamp and log lines contraints. Are these two constraints are configurable? or its kinda static limit we hardcode into Loki now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they come from the following in our limits config and are configurable per tenant as well:

# Whether or not old samples will be rejected.
# CLI flag: -validation.reject-old-samples
[reject_old_samples: <bool> | default = false]

# Maximum accepted sample age before rejecting.
# CLI flag: -validation.reject-old-samples.max-age
[reject_old_samples_max_age: <duration> | default = 336h]

# Duration for a table to be created/deleted before/after it's
# needed. Samples won't be accepted before this time.
# CLI flag: -validation.create-grace-period
[creation_grace_period: <duration> | default = 10m]


### Preprocessing

Currently the only way the distributor mutates incoming data is by normalizing labels. What this means is making `{foo="bar", bazz="buzz"}` equivalent to `{bazz="buzz", foo="bar"}`, or in other words, ensuring that the order of labels doesn't matter. This allows Loki to cache and hash them deterministically.
owen-d marked this conversation as resolved.
Show resolved Hide resolved

### Rate limiting

The distributor can also rate limit incoming logs based on the maximum per-tenant bitrate. It does this by checking a per tenant limit and dividing it by the current number of distributors. This allows the rate limit to be specified per tenant at the cluster level and enables us to scale the distributors up or down and have the per-distributor limit adjust accordingly. For instance, say we have 10 distributors and tenant A has a 10MB rate limit. Each distributor will allow up to 1MB/second before limiting. Now, say another large tenant joins the cluster and we need to spin up 10 more distributors. The now 20 distributors will adjust their rate limits for tenant A to `(10MB / 20 distributors) = 500KB/s`! This is how global limits allow much simpler and safer operation of the Loki cluster.
dannykopping marked this conversation as resolved.
Show resolved Hide resolved

**Note: The distributor uses the `ring` component under the hood to register itself amongst it's peers and get the total number of active distributors**
owen-d marked this conversation as resolved.
Show resolved Hide resolved

### Forwarding

Once the distributor has performed all of it's validation duties, it forwards data to the ingester component which is ultimately responsible for acknowledging the write.

#### Replication factor

In order to mitigate the chance of _losing_ data on any single ingester, the distributor will forward writes to a _replication_factor_ of them. Generally, this is `3`. This helps ensure that even if an ingester or two fails, we won't lose data. Loosely, for each label set (called _series_) that is pushed to a distributor, it will hash the labels and use the resulting value to look up `replication_factor` ingesters in the `ring` (which is a subcomponent that exposes a [distributed hash table](https://en.wikipedia.org/wiki/Distributed_hash_table)). It will then try to write the same data to all of them. This will error if less than a _quorum_ of writes succeed. A quorum is defined as `floor(replication_factor / 2) + 1`. So, for our `replication_factor` of `3`, we require that two writes succeed. If less than two writes succeed, the distributor returns an error and the write can be retried.
owen-d marked this conversation as resolved.
Show resolved Hide resolved

**Caveat: There's also an edge case where we acknowledge a write if 2 of the three ingesters do which means that in the case where 2 writes succeed, we can only lose one ingester before suffering data loss.**
owen-d marked this conversation as resolved.
Show resolved Hide resolved

## Why does it deserve it's own component?

Notably, the distributor is a stateless component. This makes it easy to scale and offload as much work as possible from the ingesters, which are the most critical component on the write path. The ability to independently scale these validation operations mean that Loki can also protect itself against denial of service attacks (either malicious or not) that could otherwise overload the ingesters. They act like the bouncer at the front door, ensuring everyong is appropriately dressed and has an invitation.
owen-d marked this conversation as resolved.
Show resolved Hide resolved