markdown

0 programs Taxonomy-only · no LLM program Evidence Report issue View issues
Aliases: md

Sources mentioning this language

6 sources · pl_id: pl/markdown
PldbLinguistPygmentsWikipediaHyperpolyglotWikidata · Q1193600

Wikipedia infobox

Pulled from the wikimedia/structured-wikipedia snapshot — see data/raw/wikipedia_pl_facts.*.jsonl and pl_fact.csv for the long-table provenance.

Designed byJohn MacFarlane · open source · John Gruber
First appeared2014
Homepagehttps://commonmark.org/

Extensions claimed by this language

19 claims. Each row is one upstream assertion with its strength. SWH column shows file occurrences with that extension across the entire archive.
ExtensionSourceStrengthSWH
.markdownwikidataprimary8.3M files
.mdlinguistprimary604.0M files
.mdpygmentsprimary604.0M files
.mdwikidataprimary604.0M files
.mdownwikidataprimary98.0K files
.mdtextwikidataprimary18.8K files
.mdtxtwikidataprimary537 files
.mkdwikidataprimary154.4K files
.livemdlinguistsecondary16.4K files
.markdownlinguistsecondary8.3M files
.markdownpygmentssecondary8.3M files
.mdownlinguistsecondary98.0K files
.mdwnlinguistsecondary302.3K files
.mkdlinguistsecondary154.4K files
.mkdnlinguistsecondary65.4K files
.mkdownlinguistsecondary398 files
.ronnlinguistsecondary13.3K files
.scdlinguistsecondary224.4K files
.workbooklinguistsecondary25.6K files

Programs

No programs recorded for this language yet.

Real programs from Software Heritage

1 sample mined from derived_datasets/<date>/contents/*.parquet, byte-verified against the SWH archive. Citation-grade qualified SWHIDs preserved.
2013-02-17-impact-of-heroku-routing-mesh-and-random-routing.markdown · 7301 B · ext .markdown · seen 1341× in SWH
via fallback
swh:1:cnt:472d018c324e9bab0e7cf9a09b65df2866dfc87c;origin=https://github.com/artsy/artsy.github.io;anchor=swh:1:rev:2bce54f6919d4bc267bcf9f816cad63b731327c4;path=/_posts/2013-02-17-impact-of-heroku-routing-mesh-and-random-routing.markdown
Open in SWH · Raw bytes (SWH) · GitHub raw
Show source
---
layout: post
title: The Impact of Heroku's Routing Mesh and Random Routing
date: 2013-02-17 12:21
comments: true
categories: [Heroku]
author: db
---

The [Heroku's Ugly Secret](http://rapgenius.com/James-somers-herokus-ugly-secret-lyrics) blog post went viral last week. I [wrote](http://code.dblock.org/in-defense-of-heroku) in defense of Heroku, which has now responded with an official [Routing Performance Update](https://blog.heroku.com/archives/2013/2/16/routing_performance_update/).

Random request queuing has been discussed in the past in [Tim Watson's post](http://tiwatson.com/blog/2011-2-17-heroku-no-longer-using-a-global-request-queue) based on a [response](https://groups.google.com/forum/?fromgroups=#!msg/heroku/8eOosLC5nrw/Xy2j7GapebIJ) by Heroku's Adam Wiggins. While the documentation may not have been accurate or even somewhat misleading, we, at Artsy, understood the strategy and the limitations of the routing mesh for quite sometime. Therefore, we have been making continuous efforts to improve our application's performance and reduce the negative impact of random routing inside the routing mesh over the past few months.

One thing we didn't do, was to measure the actual wait time inside a dyno. In restrospect, it seems obvious that we should have. In this post we'll describe a middleware to do so. This is entirely based on the work of [David Yeu](https://gist.github.com/daveyeu/4960893), [Jason R Clark](https://gist.github.com/jasonrclark/d82a1ea7695daac0b9ee) and RG's own [Andrew Warner](https://gist.github.com/a-warner/f5db30857ed3423cea79).

With this code in place, here's a 12 hour graph of our website's API performance. The dyno wait time for our application, in green, averaged 61.1ms for a total of 301ms average per request, which is 1/5th of the total request time. It's certainly a lot, but we do spend a lot more time in our own code.

<img src="/images/2013-02-17-impact-of-heroku-routing-mesh-and-random-routing/newrelic-12-hours.png">

Note that the single peak on the right of the graph corresponds to a dyno auto-scale job. We double the number of dynos with early morning traffic, which causes new dynos to boot up and accumulate requests before they are "warm" enough to process requests at their normal rate.

<!-- more -->

### Queue Logger Middleware

Heroku adds an `X-Request-Start` header as documented [here](https://devcenter.heroku.com/articles/http-routing) into every request it routes. We can then subtract the value of this header from `Time.now` once we're inside our code. We're also removing the the `X-Heroku-Queue-Wait-Time` header, as it's mostly zero with the current Heroku routing strategy and gets [used](https://github.com/newrelic/rpm/blame/master/lib/new_relic/agent/instrumentation/queue_time.rb#L90) as queue time by the NewRelic RPM. Finally, we're setting `env['HTTP_X_QUEUE_TIME']`, which will be picked up by NewRelic as documented [here](https://newrelic.com/docs/features/tracking-front-end-time) and adding a `X-Queue-Time` header to be able to see the queue time in every response with client tools.

```ruby config/queue_time_logger.rb
# https://gist.github.com/a-warner/f5db30857ed3423cea79
# combination of https://gist.github.com/daveyeu/4960893
# and https://gist.github.com/jasonrclark/d82a1ea7695daac0b9ee
class QueueTimeLogger
  attr_reader :app

  def initialize(app, options = {})
    @app = app
  end

  def call(env)
    now = Time.now.to_f

    env.delete("HTTP_X_HEROKU_QUEUE_WAIT_TIME")

    microseconds = (now * 1_000_000).to_i
    env["HTTP_X_MIDDLEWARE_START"] = "t=#{microseconds}"

    perf_headers = {}
    if (request_start = env["HTTP_X_REQUEST_START"])
      request_start_microseconds = request_start.gsub("t=", "").to_i * 1_000
      queue_time_microseconds = [ microseconds - request_start_microseconds, 0 ].max
      env["HTTP_X_QUEUE_TIME"] = "t=#{queue_time_microseconds}"

      queue_time_milliseconds = (queue_time_microseconds / 1_000).to_i
      perf_headers["X-Queue-Time"] = queue_time_milliseconds.to_s
    end

    status, headers, body = app.call(env)
    [ status, headers.merge(perf_headers), body ]
  end
end
```

We insert this middleware into Rails. Remember that the middleware is executed in reverse order, so you should put this in the end of your `config/environment.rb`.

```ruby config/environment.rb
require File.expand_path('../queue_time_logger', __FILE__)
config.middleware.use QueueTimeLogger
```

### Time Skew

It's important to note that since the `X-Request-Start` header is inserted by the router, we're not capturing queue wait time, we're capturing (queue wait time) + (clock skew between the router and the machine servicing the request). The time skew has a non-negligible contribution to the sum, especially that the sign of the clock skew contribution is unknown and we are replacing any negative time difference with 0. We can only hope that Heroku does a reasonable effort at synchronizing clocks between the router and the dyno servers.

### What About Dumb Routing?

One of the basic issues with one-request-at-a-time web servers and random routing is how single-threaded web servers accept connections. It sounds technically feasible that the web server could report back to the router that it's currently processing a request and have the router pick another dyno, but there're two non-trivial difficulties with implementing this.

The first is that it would require cooperation from the Heroku router, as currently, closing a TCP socket would cause it to return a 503 to the client.

The second is in the way EventMachine accepts requests in a single-threaded scenario: a request will block the EventMachine reactor, and only once it has unblocked the reactor, will it accept more requests. Those requests will sit in the TCP queue for the duration of the long request, defeating the whole concept.

### Improving Throughput on Heroku

It's important to understand that with every system you will get increasingly unfair scheduling at the load balancer when you have more than your serviceable load. To improve this on Heroku you have to either reduce the time to service each request or provision more dynos. All things considered, I think that being able to service long-running requests without any significant impact on the entire distributed system would be a luxury.

### Links

* [Queue Logger Middleware](https://gist.github.com/a-warner/f5db30857ed3423cea79)
* [Heroku's Ugly Secret](http://rapgenius.com/James-somers-herokus-ugly-secret-lyrics)
* [In Defense of Heroku](http://code.dblock.org/in-defense-of-heroku)
* [Heroku Routing Performance Update](https://blog.heroku.com/archives/2013/2/16/routing_performance_update)
* [Heroku No Longer Using a Global Request Queue](http://tiwatson.com/blog/2011-2-17-heroku-no-longer-using-a-global-request-queue)
* [How EventMachine Accepts Connections](https://groups.google.com/d/msg/thin-ruby/7p5BHt5j7M4/GnRyUP0VTzgJ)
* [Heroku HTTP Routing Documentation](https://devcenter.heroku.com/articles/http-routing)
* [NewRelic Agent Instrumentation Queue Time Implementation](https://github.com/newrelic/rpm/blame/master/lib/new_relic/agent/instrumentation/queue_time.rb#L90)
* [Tracking Front-End Time with NewRelic](https://newrelic.com/docs/features/tracking-front-end-time)

Disambiguation rules

Linguist heuristic rules that predict this language when one of its claimed extensions is shared with another.
RuleExtKindPredicates (truncated)
h/linguist/.md/0.mdpredicates[{"kind": "any", "regexes": ["(^[-A-Za-z0-9=#!\\*\\[|>])|<\\/", "\\A\\z"]}]
h/linguist/.md/2.mddefault[]
h/linguist/.scd/1.scdpredicates[{"kind": "any", "regexes": ["^#+\\s+(NAME|SYNOPSIS|DESCRIPTION)"]}]

Contribute — propose a file extension

Tell us where to find evidence about markdown (mapped to pl/markdown). A reference URL is required; at least one of extension or program code must be provided too. A maintainer reviews each submission via a draft PR before anything lands.
Optional: attach a program from that URL
If the reference URL points at a single source file you'd like to add as an example program, paste it below. The workflow will write it under languages/markdown/programs/<sha>/. Keep under ~200 lines.
(or open the pre-filled issue directly)
← markdeep Markdown Extra →