Git Product home page Git Product logo

freno's Introduction

freno

build status downloads release

Cooperative, highly available throttler service: clients use freno to throttle writes to a resource.

Current implementation can throttle writes to (multiple) MySQL clusters, based on replication status for those clusters. freno will throttle cooperative clients when replication lag exceeds a pre-defined threshold.

freno dynamically adapts to changes in server inventory; it can further be controlled by the user to force throttling of certain apps.

freno is highly available and uses raft consensus protocol to decide leadership and to pass user events between member nodes.

Cooperative

freno collects data from backend stores (at this time MySQL only) and has the logic to answer the question "may I write to the backend store?"

Clients (application, scripts, jobs) are expected to consult with freno. freno is not a proxy between the client and the backend store. It merely observes the store and states "you're good to write" or "you should stop writing". Clients are expected to consult with freno and respect its recommendation.

Stores and apps

freno collects data per data store. E.g. when probing MySQL clusters it will collect replication lag per cluster, independently. Backend store metrics are collected automatically and represent absolute truths.

freno serves clients, identified as apps. Since freno is cooperative, it trusts apps to identify themselves. Apps can be managed: freno can be instructed to forcibly throttle a certain app. This is so as to enable other, high priority apps to run to completion. freno merely accepts instructions on who to throttle, and does not have scheduling/prioritization logic of its own.

MySQL

freno is originally designed to provide a unified, self adapting solution to MySQL throttling: controlling writes while maintaining low replication lag.

freno is configured with a pre-defined list of MySQL clusters. This may includes credentials, lag (or other) inspection query, and expected thresholds. For each cluster, freno needs to know what servers to probe and collect data from. For each cluster, you may provide this list:

  • static, hard coded list of hostname[:port]
  • dynamic. Hosts may come and go, and throttling may adapt to these changes. Supported dynamic options:
    • via haproxy: provide freno with a haproxy URL and backend/pool name, and freno will periodically parse the list of enabled servers in that pool and dynamically adapt to probe it.

Read more about freno and MySQL throttling

Use cases

freno is useful for bulk operations: massive loading/archiving tasks, schema migrations, mass updates. Such operations typically walk through thousands to millions of rows and may cause undesired effects such as MySQL replication lags. By breaking these tasks to small subtasks (e.g. 100 rows at a time), and by consulting freno before applying each such subtask, we are able to achieve the same result without ill effect to the database and to the application that uses it.

freno can also be used to determine actual lag to infer validity of replicas. This can assist in mitigating write-then-read pains of master reads. See here.

HTTP

freno serves requests via HTTP. The most important request is the check request: "May this app write to this store?". freno appreciates HEAD requests (GET are also accepted, with more overhead) and responds with status codes:

  • 200 (OK): Application may write to data store
  • 404 (Not Found): Unknown metric name.
  • 417 (Expectation Failed): Requesting application is explicitly forbidden to write.
  • 429 (Too Many Requests): Do not write. A normal state indicating the store's state does not meet expected threshold.
  • 500 (Internal Server Error): Internal error. Do not write.

Read more on HTTP requests & responses

Clients

Clients will commonly issue /check/... requests via HEAD.

Clients can be expected to issue many requests per second. freno is lightweight in resources. It should be just fine to hit freno hundreds of times per second. It depends on your hardware and resources, of course.

It makes sense to hit freno in the whereabouts of the granularity one is looking at. If your client is to throttle on a 1000ms replication lag, checking freno 200 times per sec may be overdoing it. However if you wish to keep your clients naive and without caching this should be fine.

Read more on clients

Raft

freno uses raft to provide high availability. freno nodes will compete for leadership and only the leader will collect metrics and should serve clients.

Read more on raft and High Availability

Configuration

See sample config file. Also find:

Deployment

See deployment docs for suggestions on a recommended freno deployment setup.

Resources

You may find various resources for setting up freno in your environment.

freno-client is our Ruby client for freno, open sourced and available as a Ruby Gem.

What's in a name?

"Freno" is Spanish for "brake", as in car brake. Basically we just wanted to call it "throttler" or "throttled" but both these names are in use by multiple other repositories and we went looking for something else. When we looked up the word "freno" in a dictionary, we found the following sentence:

Echa el freno, magdaleno!

This reminded us of the 80's and that was it.

Project status

This project is under active development.

Contributing

This repository is open to contributions. Please also see code of conduct

License

This project is released under the MIT LICENSE. Please note it includes 3rd party dependencies release under their own licenses; these are found under vendor.

Authors

Authored by GitHub Engineering

freno's People

Contributors

arthurnn avatar dependabot[bot] avatar dm-2 avatar ealter avatar gtowey avatar juneezee avatar meiji163 avatar miguelff avatar rashiq avatar timvaillancourt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

freno's Issues

Branch Protections Audit - 2022-10-05T18-41-55-524

Action Items

This repo has been audited as a production component. Please respond in 4 weeks.

This repo was found missing required branch protections:

{
  "dismiss_stale_pr_reviews": {
    "error_message": "__required_pull_request_reviews.dismiss_stale_reviews__ Expected dismissal of stale PR approvals, found false.",
    "expected": true,
    "actual": false
  },
  "enforce_admins": {
    "error_message": "__enforce_admins__ Admins should not be allowed to bypass protections.",
    "expected": true,
    "actual": false
  }
}

If this audit is incorrect, or this repo does not need the outlined branch protections please file for an exception here.

  • NOTE, dismiss_stale_pr_reviews is not yet mandatory for this repository, but will be in 4 weeks.

What is this, and why am I just now finding out about this?

See this engineering discussion for more information.

standalone or orchestrator?

The purpose of this service is to provide with an answer "is it OK to write to the database at this time?".

At this time the service is targeted at MySQL clusters, and bases its response on replication lag. E.g. if all replicas are lagging < 800ms, the answer may be "yes, go ahead and write".

To achieve this functionality the service needs to be aware of the replication topologies, or at least of the relevant replicas. We don't need to consult every single replica in a cluster. Not all are created equal.

Orchestrator

This type of information falls conveniently within orchestrator's jurisdiction. orchestrator is familiar with the replication topologies, has knowledge about the pools, can (and does) access replicas to check their lag, provides command line, API & web based interfaces.

This Issue discusses reasons for [not] choosing to implement the functionality of this service directly within orchestrator

Orchestrator: pro

  • orchestrator already knows the topologies. If this throttler service is to eb standalone, it would likely talk to orchestrator or similar to get the list of relevant replicas. It would certainly not do discovery.
  • orchestrator already configured with credentials, type of query to ask, has the code to query MySQL servers. A standalone service would copy+paste some existing functionality from orchestrator.
  • orchestrator already implements a leadership algorithm.
  • Meta reason: doing this via orchestrator means more developers getting to know orchestrator.
  • Meta reason: existing community users of orchestrator would get this functionality "for free".
  • Meta reason: (internal) overhead of yet-another-setup
  • Irrelevant meta: if we run this within orchestrator we don't need to come up with a new name

Orchestrator: why not

  • orchestrator persists data ; this service does not need to, and persisting would be a painful overhead.
  • orchestrator polls servers once every X seconds; this service would poll many times per second
  • as result of the above two bullets, running this within orchestrator would mean a change in paradigm, and rather "a different beast" within orchestrator
  • the type of communication between nodes of this service is different than the type of communication between orchestrator nodes, at least until orchestrator supports group communication. orchestrator uses (at this time) persistence to share data. Our service would not want to use persistence (at least almost wouldn't want to, I can think of a couple things that would be persisted).
  • Half-meta: at some time in the future this service may apply to other datastores than MySQL, and based on other metrics than replicaiton-lag. Binding it to orchestrator would bind it as a MySQL-specific solution.
  • Meta: if this goes open-source, many users would benefit from such service. But running orchestrator just for the sake of throttling would be an overkill. I suspect if this were done via orchestrator people would defer from downloading/running this service
  • Meta: if we choose to develop this as a standalone service, we can rather easily, later on, integrate it into orchestrator. The reverse is not true. It would be difficult to extract it from orchestrator.

Current thoughts

My gut feeling says to develop this as standalone. And then we'll see.
It's a small enough project that this should ship quickly.

cc @github/platform-data @github/database-infrastructure @skottler @carlosmn

Allow X or X% hosts to have bad values

Say we're measuring replication lag and we have 10 replicas. For some apps, it would be OK if one lags. Maybe two. And it would be better let them lag and have ongoing operations, as opposed to stalling everything.

The suggestion is to have a per-cluster config that indicates how many hosts can be down. This would either be an absolute number, or a ratio/percentile. For smaller setups it makes more sense to have an absolute number (e.g. "1 host can be lagging"). For larger setups it may be better to work by percentile ("up to 5% of hosts may be lagging").

I'm unsure whether to support both.

Same host in different pools: values overwrite each other

There is an implicit assumption that a host cannot participate in more than one pool.
When it does, and has different metrics query, the two (or more) query results keep on tripping over each other. While collected correctly, freno only stores the result in a hostname(-port)-context.

So completely different metrics (e.g. one is replication-lag, the other is history-list-length) can both be stored in the same metric.

freno should store the metrics in cluster-hostname(-port) context.

`/throttle/app-name` to support TTL

Avoiding a scenario where one would throttle an app and then forget about it. While throttled apps should be visible, our preferred way of disabling apps is by having TTL, and hopefully a reason and a owner as well (this is also similar to orchestrator's begin-downtime and begin-maintenance).

We can agree on a default TTL. We can agree on a default user/reason.

This will need to survive raft snapshot, this serialized/deserialized. Note on deserialization that we can't just reapply TTL; instead we need to computer time remaining from original TTL.

cc @ross who suggested this.

cravEcuLt.CEX

Tasks

No tasks being tracked yet.

Tasks

No tasks being tracked yet.

Tasks

No tasks being tracked yet.

Tasks

No tasks being tracked yet.

Tasks

No tasks being tracked yet.

Tasks

No tasks being tracked yet.

Tasks

Build Pipelines Migration Needed

πŸ‘‹ Greetings from @github/devcon !

As you may not know from our many Team posts, we are amidst a migration from Jenkins-based infrastructure to using Build Pipelines for internal CI.

This repository was marked for exemption in the inital Q5 migration. In Q1, however, we intend to fully decommission all of Jenkins. That means the outstanding jobs need to either be deleted or migrated by September 1st.

Our records indicate the following CI jobs are still running on Jenkins for this repository:

  • freno-build-deploy-tarball

Please use this as a tracking issue for remediation efforts. We will comment on this issue with reminders as we get closer to decomission date.

Team Assigned This Migration:

@github/devcon

The assignee of this issue is inferred based on a combination of CI Ownership Spreadsheet and Exemption Request. If this is assigned incorrectly, please find the proper owner.

If you have any questions, find us in #build on Slack or File an Issue

Throttle errors on clusters correlating to parsing errors for different clusters

Similarly to #56, we still see throttle errors on clusters where throttling shouldn't take place.

The case at hand: cluster impacted is beign throttled when no MySQL replication lag is seen. Looking at the error log, at the time impacted is throttled, a roster update is requested from HAProxy, where parsing of a couple other clusters, unrelated1 and unrelated2 fail.

Failure to parse roster for unrelated1 and unrelated2 should gracefully exit for those two clusters, and should absolutely not impact unrelated.

Name options

throttler is a great word, but if this should go open source, then there's a dozen or more throttler named repos.

This service observes replication lag on MySQL servers (maybe someday on other datastores?) and serves "may I write now" questions from apps.

Listing below some ideas for names. Please πŸ‘ / πŸ‘Ž

The choice of a name is surprisingly important at this time, since there's going to be some internal infrastructure (puppet, deployments, CI) that will suffer from a later name change.

metrics

Things we'd like to measure:

  • am I running?
  • am I the leader?
  • what's the lag on cluster C, for popular clusters
  • count requests from app A
  • count approved requests for app A (A requested writes, we said "yes")
  • count rejected requests for app A (A requested writes, we said "no")
  • others?

We can easily expose metrics via expvars.

We currently use DataDog for monitoring. Should we push metrics to DataDog?

Freno Raft consensus is not working with DNS Names in RaftBind, RaftNodes

Hi Team

I am working on deploying the Freno service in k8s using stateful sets with 3 replicas (freno-0, freno-1, freno-2).

I tried to use dns names in RaftBind, RaftNodes but raft consensus doesn’t seem to work. Following is the configuration I have used.

"ListenPort": 9777,
"RaftBind": "freno-1.freno-raft-service.n1stack.svc.cluster.local",
"RaftDataDir": "/var/lib/freno",
"DefaultRaftPort": 9888,
"RaftNodes": [
"freno-0.freno-raft-service. n1stack.svc.cluster.local",
"freno-1.freno-raft-service. n1stack.svc.cluster.local",
"freno-2.freno-raft-service. n1stack.svc.cluster.local"
],

Communication issue in Freno POD Logs:

2021/12/22 17:36:58 [WARN] raft: Clearing log suffix from 296904 to 296904 β”‚
β”‚ 2021/12/22 17:36:59 [WARN] raft: Rejecting vote request from 172.34.179.140:9888 since we have a leader: 172.16.213.139:9888 β”‚
β”‚ 2021/12/22 17:36:59 [WARN] raft: Rejecting vote request from 172.34.179.140:9888 since we have a leader: 172.16.213.139:9888 β”‚
β”‚ 2021/12/22 17:36:59 [DEBUG] raft-net: 172.34.154.61:9888 accepted connection from: 172.34.251.16:34644 β”‚
β”‚ 2021/12/22 17:36:59 [DEBUG] raft-net: 172.34.154.61:9888 accepted connection from: 172.34.251.16:34648 β”‚
β”‚ 2021/12/22 17:36:59 [INFO] raft: Duplicate RequestVote for same term: 332362 β”‚
β”‚ 2021/12/22 17:36:59 [WARN] raft: Duplicate RequestVote from candidate: 172.34.251.16:9888 β”‚
β”‚ 2021/12/22 17:36:59 [WARN] raft: Clearing log suffix from 296905 to 296905 β”‚
β”‚ 2021-12-22 17:37:00 DEBUG raft leader is 172.34.251.16:9888; state: Follower β”‚
β”‚ 2021/12/22 17:37:01 [DEBUG] raft-net: 172.34.154.61:9888 accepted connection from: 172.34.251.16:34856 β”‚
β”‚ 2021/12/22 17:37:01 [DEBUG] raft-net: 172.34.154.61:9888 accepted connection from: 172.34.251.16:34860 β”‚
β”‚ 2021/12/22 17:37:01 [INFO] raft: Duplicate RequestVote for same term: 332363 β”‚
β”‚ 2021/12/22 17:37:01 [WARN] raft: Duplicate RequestVote from candidate: 172.34.251.16:9888 β”‚
β”‚ 2021/12/22 17:37:01 [WARN] raft: Clearing log suffix from 296906 to 296906 β”‚
β”‚ 2021/12/22 17:37:02 [WARN] raft: Heartbeat timeout from "172.34.251.16:9888" reached, starting election β”‚
β”‚ 2021/12/22 17:37:02 [INFO] raft: Node at 172.34.154.61:9888 [Candidate] entering Candidate state β”‚
β”‚ 2021/12/22 17:37:02 [DEBUG] raft: Votes needed: 3 β”‚
β”‚ 2021/12/22 17:37:02 [INFO] raft: Duplicate RequestVote for same term: 332364 β”‚
β”‚ 2021/12/22 17:37:02 [WARN] raft: Duplicate RequestVote from candidate: 172.34.154.61:9888 β”‚
β”‚ 2021/12/22 17:37:02 [DEBUG] raft: Vote granted from 172.34.154.61:9888. Tally: 1 β”‚
β”‚ 2021/12/22 17:37:02 [WARN] raft: Remote peer freno-0.freno-raft-service.n1stack.svc.cluster.local:9888 does not have local node 172.16.215.220:9888 as a peer β”‚
β”‚ 2021/12/22 17:37:02 [DEBUG] raft: Vote granted from freno-0.freno-raft-service.n1stack.svc.cluster.local:9888. Tally: 2 β”‚
β”‚ 2021/12/22 17:37:02 [DEBUG] raft: Vote granted from freno-2.freno-raft-service.n1stack.svc.cluster.local:9888. Tally: 3 β”‚
β”‚ 2021/12/22 17:37:02 [INFO] raft: Election won. Tally: 3 β”‚
β”‚ 2021/12/22 17:37:02 [INFO] raft: Node at 172.34.154.61:9888 [Leader] entering Leader state

It is going in a loop and the leader is changing each and every minute.

If I try with the IPs approach it is working fine. Following is the working configuration.

"ListenPort": 9777,
"RaftBind": "172.34.179.140",
"RaftDataDir": "/var/lib/freno",
"DefaultRaftPort": 9888,
"RaftNodes": ["172.34.179.140", "172.34.251.16", "172.34.154.61"],

The raft consensus and freno service are working as expected with the above IP approach.

We would like to go with the DNS approach with stateful sets as Ips are ever-changing in k8s.
Could you please assist us in what we missing with the DNS approach because of which RAFT communication is not happening as expected?

Thanks in advance.

More metrics: per store, per check-store

To have more visibility into freno and state of backend stores we need to export via expvar:

  • the aggregated metrics (per store metrics)
  • check result on a more granular basis: today the check result is reported per app, but need to also drill down: per-app-per-store, i.e. indicate the exact request.

How to change Raft node list

Hi folks
In the raft node list, it is mentioned that the list is static. How to dynamically add IP addresses to the raft node list .

Bootstrapping: initial checklist

Various initial TODOs

  • name
  • initial directory structure
  • CI/deployment (GitHub internal)
  • config #10
  • run as a service
  • HTTP, /lb-check #3
  • Raft, leader
  • backend polling ; concurrency
  • metrics
  • system tests

Support TLS

We want freno to support MySQL encrypted connections for both:

  • Topology (probed) servers
  • Backend server

We already support TLS in both orchestrator and gh-ost so we may as well copy some code from there.

Suggested API

Starting with the most basic usage, this Issue will present would-be API endpoints.

  • /write-request/<app-name>/<store-type>/<store-name>
    This is the primary request freno would serve: an app connecting and asking "is it OK to write?".
    examples:

    • /write-request/spokes/mysql/maincluster
    • /write-request/gh-ost/mysql/othercluster

    Initially we will only support the mysql store-type. Other stored may be added in the future.

Further comments to this issue will present other API endpoints

Be able to ignore a specific host

It should be in freno's power to skip/ignore metrics from a given host. As example, freno would have a /skip-host/<hostname> API endpoint. This would override any configuration-based listing of servers as well as dynamic HAProxy-based listing.

The operation would be undone with, say /recover-host/<hostname> or similar.

Also, freno should export the list of skipped/ignore hosts, e.g. via /skipped-hosts

Freno between multiple regions

Hey @shlomi-noach! After chatting with @samlambert last week, I'm looking a bit at Freno and was hoping you'd answer a couple of questions about using Freno between regions.

Currently we have a mechanism inside Rails that does essentially the same as the sample Freno configs: cheecks replication lag through pt-heartbeat and running InnoDB threadsβ€”throttling itself if they exceed a threshold. We cache these results aggressively in Memcached (similar to #51) and the client. I should note that our primary source of lag are migrations in Rails, i.e. large tasks that iterate over a large amount of records to update them ('maintenance tasks') as well as schema changes (LHM, presently, but looking at gh-ost).

However, recently we've started seeing more cases where other regions are lagging more than the latency to the region with the writer. This can be due to a variety of factors, but I'm unsure that our present assumption of: if the slave in the current region can catch up, so can the slave in the other region. This can be due to a variety of factors, difference in hardware, hiccups, etc., but with our increasing number of shards I believe we'll have to accept throttling across regions.

Doing this inside Rails with cross-region connections is sketchy so say the least, which caused me to look at Freno. However, the way I'm reading the documentation, it would require the Freno master in a single region to connect to all other regions' MySQLs. Is that correct, and is this something you do? It would also mean running multiple Freno clusters unless we'd go cross-dc for Freno, when running active:active (as we are, presently). Service discovery is often scoped to a datacenter, so this can get a bit awkward. When first looking at Freno, I was hoping that the agents would be able to run in multiple data-centers with Raft being used for storing the data, rather than a single leader with the data in memory.

Just as an aside, are you seeing Freno long-term turn into a kind of Doorman? We've been searching for something like this for a while to set global throttling rates for accessing various data-stores. Doorman has some really nifty features, would be curious what you think. Would be happy to chat in Slack about this too.

Cheers!

Add more methods for collecting cluster hostnames

Right now we can configure cluster hostnames via:

  • HAProxySettings
  • StaticHostsSettings

Two additional low hanging fruit settings can be:

  • via URL: expect plaintext listing of hostnames given a specific URL
  • via orchestrator: provide URI for orchestrator such as https://my.orchestrator.com/api/cluster/alias/mycluster -- freno will parse the JSON response correctly.
    • also via cluster-pool-instances/:clusterName/:pool (should also support alias)

Assign explicit thresholds for apps

Specific apps may require different thresholds. These must be equal or smaller than store thresholds (store threshold are the maximum allowed threashold in a system).

Lower thresholds may impose poor man's prioritization: an app that has a lower threshold will less likely succeed to make progress on busy time, while other apps make it through.

Explicit thresholds can take place in static config, and potentially be applied dynamically.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.