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

Health endpoint verifies Druid brokers' health and readiness #99

Merged
merged 7 commits into from
Apr 20, 2018
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ turnilo --druid broker_host:broker_port

* [Configuration](docs/configuration.md)
* [Generating Links](docs/generating-links.md)
* [Health checking](docs/health-checking.md)

## Development

Expand Down
8 changes: 8 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ A custom path to act as the server string.

The Turnilo UI will be served from `http://turnilo-host:$port/` and `http://turnilo-host:$port/$serverRoot`

**healthEndpoint** (string), default "/health"

A health endpoint location. See [Checking health of Turnilo instance](health-checking.md)

**iframe** ("allow" | "deny"), default "allow"

Specify whether Turnilo will be allowed to run in an iFrame.
Expand Down Expand Up @@ -97,6 +101,10 @@ Define this to override the automatic version detection.

The timeout to set on the queries in ms.

**healthCheckingTimeout** (number), default: 1000

The timeout for the cluster health checking request in ms. See [Checking health of Turnilo instance](health-checking.md)

**sourceListScan** ("auto" | "disable"), default: "auto"

Should the sources of this cluster be automatically scanned and new sources added as data cubes.
Expand Down
56 changes: 56 additions & 0 deletions docs/health-checking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Checking health of Turnilo instance

Turnilo instance's health is defined in terms of being able to communicate with all configured Druid brokers
and those brokers knowing about all segments in Zookeeper.

It can be checked by sending a GET request to a `healthEndpoint` path defined in Turnilo's server [configuration](configuration.md).

Healthy Turnilo instance responds with HTTP status 200 while an unhealthy one responds with the status of 500.
Copy link
Member

Choose a reason for hiding this comment

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

I would return 503 as a more specific error.

The body of a response contains health status of all configured brokers with optional error message on unhealthy brokers.

While processing the health checking request Turnilo server will send its own requests to all configured
druid clusters' brokers for `/druid/broker/v1/loadstatus` endpoint. It will check that all brokers responds within
individually defined cluster timeout (`healthCheckingTimeout` property in [cluster properties](configuration.md#general-properties))
and that the response body contains `inventoryInitialized` flag set to `true`.
If any of the requests to brokers fail to meet the criteria defined above the Turnilo instance is marked as unhealthy.

# Response examples

Healthy response example:
```
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"clusters": [
{
"host": "localhost:8082",
"message": "",
"status": "healthy"
}
],
"status": "healthy"
}
```

Unhealthy response example:
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json; charset=utf-8

{
"clusters": [
{
"host": "localhost:8082",
"message": "inventory not initialized",
"status": "unhealthy"
},
{
"host": "192.168.99.100:8082",
"message": "connection error: 'Error: ESOCKETTIMEDOUT'",
"status": "unhealthy"
}
],
"status": "unhealthy"
}
```
100 changes: 100 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
"react-dom": "16.2.0",
"react-syntax-highlighter": "7.0.2",
"react-copy-to-clipboard": "5.0.1",
"request": "2.83.0"
"request": "2.83.0",
"request-promise-native": "1.0.5"
},
"devDependencies": {
"@types/body-parser": "1.16.8",
Expand All @@ -100,22 +101,24 @@
"@types/mime": "2.0.0",
"@types/mocha": "2.2.46",
"@types/moment-timezone": "0.5.4",
"@types/nock": "9.1.3",
"@types/node": "8.5.2",
"@types/nopt": "3.0.29",
"@types/numeral": "0.0.22",
"@types/q": "1.0.6",
"@types/qajax": "0.0.29",
"@types/react": "16.0.40",
"@types/react-copy-to-clipboard": "4.2.5",
"@types/react-dom": "16.0.4",
"@types/react-syntax-highlighter": "0.0.5",
"@types/react-transition-group": "2.0.7",
"@types/request-promise-native": "^1.0.14",
"@types/rewire": "2.5.28",
"@types/sinon": "4.1.4",
"@types/superagent": "3.5.6",
"@types/supertest": "2.0.4",
"@types/webpack": "3.8.10",
"@types/webpack-env": "1.13.3",
"@types/react-syntax-highlighter": "0.0.5",
"@types/react-copy-to-clipboard": "4.2.5",
"awesome-typescript-loader": "3.4.1",
"chai": "4.1.2",
"create-react-class": "15.6.2",
Expand All @@ -125,6 +128,7 @@
"immutable-class-tester": "0.5.12",
"jsdom": "9.4.2",
"mocha": "4.1.0",
"nock": "9.2.5",
"node-sass": "4.7.2",
"npm-run-all": "4.1.2",
"react-hot-loader": "3.1.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ describe('ImmutableInput', () => {
});

it('works for valid values', () => {
expect(node.value).to.equal('DRUID');
expect(node.value).to.equal('DRUID-TWITTER');

node.value = 'GIRAFFE';
TestUtils.Simulate.change(node);
Expand All @@ -157,7 +157,7 @@ describe('ImmutableInput', () => {
});

it('works when an error is thrown', () => {
expect(node.value).to.equal('DRUID');
expect(node.value).to.equal('DRUID-TWITTER');

node.value = 'PLATYPUS';
TestUtils.Simulate.change(node);
Expand Down
8 changes: 4 additions & 4 deletions src/common/models/app-settings/app-settings.mocha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('AppSettings', () => {
it("errors if there is no matching cluster", () => {
var js = AppSettingsMock.wikiOnlyJS();
js.clusters = [];
expect(() => AppSettings.fromJS(js, context)).to.throw("Can not find cluster 'druid' for data cube 'wiki'");
expect(() => AppSettings.fromJS(js, context)).to.throw("Can not find cluster 'druid-wiki' for data cube 'wiki'");
});

});
Expand Down Expand Up @@ -90,7 +90,7 @@ describe('AppSettings', () => {
druidHost: '192.168.99.100',
sourceListScan: 'disable',
dataSources: [
DataCubeMock.WIKI_JS
{ ...DataCubeMock.WIKI_JS, clusterName: "druid" }
]
};

Expand Down Expand Up @@ -122,7 +122,7 @@ describe('AppSettings', () => {
expect(settings.toClientSettings().toJS()).to.deep.equal({
"clusters": [
{
"name": "druid",
"name": "druid-wiki",
"type": "druid"
}
],
Expand Down Expand Up @@ -159,7 +159,7 @@ describe('AppSettings', () => {
"unsplitable": true
}
],
"clusterName": "druid",
"clusterName": "druid-wiki",
"defaultDuration": "P3D",
"defaultFilter": {
"op": "literal",
Expand Down
Loading