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

[savedObjects] Use index template #14271

Merged
merged 18 commits into from
Nov 22, 2017

Conversation

spalger
Copy link
Contributor

@spalger spalger commented Oct 3, 2017

  • Remove Kibana index creation from the health check
  • Only patchKibanaIndex() when it exists
  • Convert the SavedObjectsClient's onBeforeWrite() handler so that every write PUTs the index template to elasticsearch
  • Respond with a special 503 when SavedObjectsClient#create fails because of action.auto_create_index: false
  • Render informational error when action.auto_create_index: false prevents Kibana from being able to saved objects

image

@spalger spalger added blocked v6.1.0 v7.0.0 WIP Work in progress Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc labels Oct 3, 2017
@spalger spalger changed the title [savedObjects] Use template for savedObjects index [savedObjects] Use index template Oct 3, 2017
The elasticsearch plugin currently checks for the Kibana index on each iteration of the healthCheck, and creates it if it does not exist. This removes that step from the healthCheck and instead, before each savedObject is written to elasticsearch, ensures that Elasticsearch has the necessary index template should the write result in index creation.

The healthCheck still has the `patchKibanaIndex()` logic, which checks the type in the Kibana index and adds any missing types. This step now does nothing when the Kibana index does not exist, and does what it has always done when it does.
@spalger spalger force-pushed the implement/kibana-index-template branch from 531dec1 to 10e920a Compare October 30, 2017 20:14
@spalger spalger added review and removed blocked WIP Work in progress labels Oct 30, 2017
@tylersmalley
Copy link
Contributor

Looks like the tests are failing for no such index.

(cherry picked from commit b1ef897dafeb6d43fe279776e44a9d793a389dc3)
@spalger spalger force-pushed the implement/kibana-index-template branch from b1ef897 to 825f2e9 Compare October 31, 2017 20:15
import kibanaVersion from './kibana_version';
import { ensureEsVersion } from './ensure_es_version';
import { ensureNotTribe } from './ensure_not_tribe';
import { patchKibanaIndex } from './patch_kibana_index';
import { format } from 'util';
Copy link
Member

Choose a reason for hiding this comment

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

optional nit: I know it was here before (hence up to you), but it seems the only place it's used in is:

plugin.status.red(format('Unable to connect to Elasticsearch at %s.', url));

that is basically just

plugin.status.red(`Unable to connect to Elasticsearch at ${url}`);

sinon.assert.notCalled(plugin.status.red);
sinon.assert.calledOnce(plugin.status.green);

expect(plugin.status.green.args[0][0]).to.be('Kibana index ready');
expect(plugin.status.green.args[0][0]).to.be('Ready');
Copy link
Member

Choose a reason for hiding this comment

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

nit: calledWith here and in the test below? :)

@@ -1,25 +0,0 @@
export default function (server) {
Copy link
Member

@azasypkin azasypkin Nov 3, 2017

Choose a reason for hiding this comment

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

question: I couldn't find any comments about this "feature", but just in general why did we need this previously and why we don't need this anymore? Just for my understanding.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What I call the "saved objects index" used to be the "Kibana index", which needed to be created before the first write or elasticsearch would automatically create the index with automatic (and likely incorrect) mappings.

This part of the health checker was responsible for doing that, and we would run it on an interval, but this new strategy teaches elasticsearch how to automatically create the index correctly instead.

Copy link
Member

Choose a reason for hiding this comment

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

Got it, thanks

@@ -25,6 +25,12 @@ export async function patchKibanaIndex(options) {

const rootEsType = getRootType(kibanaIndexMappingsDsl);
const currentMappingsDsl = await getCurrentMappings(callCluster, indexName, rootEsType);

// patchKibanaIndex() should do nothing if there are no current mappings
Copy link
Member

Choose a reason for hiding this comment

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

nit: would you mind adding a simple unit test to make sure we handle 404 properly and don't fail unexpectedly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -82,6 +82,19 @@ export function isEsUnavailableError(error) {
}


// 503 - Unable to automatically create index because of action.auto_create_index setting
const CODE_ES_AUTO_CREATE_INDEX_ERROR = 'SavedObjectsClient/autoCreateIndex';
export function createEsAutoCreateIndexError() {
Copy link
Member

Choose a reason for hiding this comment

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

nit: it seems every error here has a corresponding unit test suit, so it would be great if you can add one for this new error type as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@azasypkin
Copy link
Member

jenkins, test this

@@ -30,30 +30,34 @@ export function savedObjectsMixin(kbnServer, server) {
const adminCluster = server.plugins.elasticsearch.getCluster('admin');

try {
await adminCluster.callWithInternalUser('cluster.health', {
timeout: `${server.config().get('savedObjects.indexCheckTimeout')}ms`,
Copy link
Member

Choose a reason for hiding this comment

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

nit: Seems like indexCheckTimeout is not used anymore, if so could you please remove it completely? I see it only in the config schema (it looks like it's the only property savedObjects config node) and in a single test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

},
err: {
message: error.message,
stack: error.stack,
Copy link
Member

Choose a reason for hiding this comment

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

nit: let's be consistent on the trailing commas within this file :)

Copy link
Contributor Author

@spalger spalger Nov 7, 2017

Choose a reason for hiding this comment

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

So ready for #14631

index: server.config().get('kibana.index'),
waitForStatus: 'yellow',
const index = server.config().get('kibana.index');
await adminCluster.callWithInternalUser('indices.putTemplate', {
Copy link
Member

Choose a reason for hiding this comment

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

question: it must be a dumb question, but why do we want to put this template on every write? As far as I understand server.getKibanaIndexMappingsDsl() is supposed to return the same thing most of the time (always?) or am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a dumb question. My intention was to ensure that Elasticsearch has the index template before every write so that even if a user deletes the index and index template in elasticsearch, writes will still work (unless the index template is deleted in the tiny amount of time between putting the index template and indexing the document).

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the explanation! Then optional nit: maybe you can add this comment to the code or even move this piece of code to a dedicated function like ensureKibanaIndexExists or something like that to make intention clearer (your comment above still seems to be useful to have there as well)?

@@ -0,0 +1,3 @@
.error-multi-allow-explicit-index {
Copy link
Member

Choose a reason for hiding this comment

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

nit: did you mean error-auto-create-index or something similar?


return new SavedObjectsClient({
$http,
basePath: chrome.getBasePath(),
Copy link
Member

Choose a reason for hiding this comment

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

nit: you can probably omit basePath property since it's the same as default one.


// loose ISO8601 UTC time with milliseconds validation
expect(resp.body).to.have.property('updated_at').match(/^[\d-]{10}T[\d:\.]{12}Z$/);

expect(resp.body).to.eql({
Copy link
Member

Choose a reason for hiding this comment

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

optional nit: not really better, but just another way of using eql with object that has properties that aren't know upfront:

expect(resp.body).to.eql({
  ...resp.body,
  type: 'visualization',
  version: 1,
  attributes: {
    title: 'My favorite vis'
  }
});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, I really wanted to list all expected properties

@tylersmalley
Copy link
Contributor

Jenkins, test it

Failure:

18:10:04    └- ✖ fail: "console app "before all" hook"
18:10:04    │        [POST http://localhost:9515/session/29b51f08a0352993ba8f3f1f172fc37f/window/current/size / {"width":1200,"height":800}] disconnected: unable to connect to renderer
18:10:04    │         (Session info: chrome=62.0.3202.75)
18:10:04    │         (Driver info: chromedriver=2.32.498513 

@tylersmalley
Copy link
Contributor

Overall, looks good - just a couple things I came across:

In x-pack, when ES is unable I get 401 unauthorized

curl -X POST -H "Content-Type: application/json" -H "kbn-xsrf: true" -d '{ "attributes": { "title": "logstash-*" } }' http://elastic:changeme@localhost:5601/api/saved_objects/index-pattern
{"statusCode":401,"error":"Unauthorized”}

This differs from {"statusCode":503,"error":"Service Unavailable","message":"Service Unavailable”} in OS Kibana


Have we measured what effect putting the index template before each write has on object imports? Could it be debounced?


When starting Kibana without an index:

server    log   [00:32:54.002] [warning][kibana-monitoring][monitoring-ui] [index_not_found_exception] no such index, with { resource.type="index_or_alias" & resource.id=".kibana" & index_uuid="_na_" & index=".kibana" } :: {"path":"/.kibana/_search","query":{"filter_path":"aggregations.types.buckets"},"body":"{\"size\":0,\"query\":{\"bool\":{\"should\":[{\"term\":{\"type\":{\"value\":\"graph-workspace\"}}},{\"term\":{\"type\":{\"value\":\"timelion-sheet\"}}}]}},\"aggs\":{\"types\":{\"terms\":{\"field\":\"type\",\"size\":2}}}}","statusCode":404,"response":"{\"error\":{\"root_cause\":[{\"type\":\"index_not_found_exception\",\"reason\":\"no such index\",\"resource.type\":\"index_or_alias\",\"resource.id\":\".kibana\",\"index_uuid\":\"_na_\",\"index\":\".kibana\"}],\"type\":\"index_not_found_exception\",\"reason\":\"no such index\",\"resource.type\":\"index_or_alias\",\"resource.id\":\".kibana\",\"index_uuid\":\"_na_\",\"index\":\".kibana\"},\"status\":404}"}
    at respond (/Users/tyler/elastic/kibana/node_modules/elasticsearch/src/lib/transport.js:295:15)
    at checkRespForFailure (/Users/tyler/elastic/kibana/node_modules/elasticsearch/src/lib/transport.js:254:7)
    at HttpConnector.<anonymous> (/Users/tyler/elastic/kibana/node_modules/elasticsearch/src/lib/connectors/http.js:159:7)
    at IncomingMessage.bound (/Users/tyler/elastic/kibana/node_modules/elasticsearch/node_modules/lodash/dist/lodash.js:729:21)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:80:11)
    at process._tickCallback (internal/process/next_tick.js:104:9)

Copy link
Contributor

@tylersmalley tylersmalley left a comment

Choose a reason for hiding this comment

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

Couple questions/concerns with x-pack.

@tylersmalley
Copy link
Contributor

I keep coming back to questioning if setting the index template before every write is too heavy, or necessary. What does it prevent against?

  • If they delete the Kibana index, then the index template set (on startup?) would define the mapping.
  • If they swap out the Elasticsearch instance behind it, then they will get dynamic mappings but they are also missing their data. Can we identify when this happens?

@spalger
Copy link
Contributor Author

spalger commented Nov 9, 2017

@tylersmalley I don’t think you should worry about it, the most expensive part of that request is probably serializing the JSON body. Any sort of conditional “do I need to put the index template?” logic would probably just muddy up the super simple implementation. That said, I’ll test the impact on object imports and see if it’s worth fixing

@spalger
Copy link
Contributor Author

spalger commented Nov 9, 2017

In x-pack, when ES is unable I get 401 unauthorized

curl -X POST -H "Content-Type: application/json" -H "kbn-xsrf: true" -d '{ "attributes": { "title": "logstash-*" } }' http://elastic:changeme@localhost:5601/api/saved_objects/index-pattern
{"statusCode":401,"error":"Unauthorized”}

This is the same behavior as master, although I think we should probably ensure it is a 500 instead.

@spalger
Copy link
Contributor Author

spalger commented Nov 9, 2017

Ran a test import with 110 saved objects and observed the difference between master/this branch. First thing I noticed is that writes are already "slow" because we wait for index refresh.

In master it took 20.31 seconds and in this branch it to 20.11 seconds, the variance is really just random based on the time it takes to refresh the es index.

@@ -99,6 +99,7 @@ export class UiSettingsService {
try {
await this._savedObjectsClient.update(this._type, this._id, changes);
} catch (error) {
debugger;
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you mean to commit this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope!

@spalger spalger force-pushed the implement/kibana-index-template branch from c1c6942 to e593bd9 Compare November 14, 2017 23:02
@elastic elastic deleted a comment from kimjoar Nov 21, 2017
Copy link
Member

@jbudz jbudz left a comment

Choose a reason for hiding this comment

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

LGTM

I tested swapping out the kibana.index, and using aliases along with a bunch of general crud operations. I think the template will give us a softer mapping failure in cases when we previously would try to patch, so 👍 to that.

I'm iffy with pushing the template on so many requests but I'm rationalizing it's probably cheaper than the polling health check over time.

@spalger spalger merged commit 90e2aa0 into elastic:master Nov 22, 2017
spalger added a commit to spalger/kibana that referenced this pull request Nov 22, 2017
* [es][savedObjects/index] put template on each savedObject write

The elasticsearch plugin currently checks for the Kibana index on each iteration of the healthCheck, and creates it if it does not exist. This removes that step from the healthCheck and instead, before each savedObject is written to elasticsearch, ensures that Elasticsearch has the necessary index template should the write result in index creation.

The healthCheck still has the `patchKibanaIndex()` logic, which checks the type in the Kibana index and adds any missing types. This step now does nothing when the Kibana index does not exist, and does what it has always done when it does.

* [ftr] remove unused kibanaIndex service

(cherry picked from commit b1ef897dafeb6d43fe279776e44a9d793a389dc3)

* [savedObjects/integration] create now creates kibana index

* [es/healthCheck] remove use of format()

* [es/healthCheck/tests] use sinon assertions

* [es/patchKibanaIndex] test for kibana index missing behavior

* [savedObjects/errors] add tests for EsAutoCreateIndexError

* [savedObjects/config] deprecate and remove savedObjects.indexCheckTimeout config

* use dangling commas consistently

* [ui/error_auto_create_index] fix class names

* [ui/savedObjectsClient] no need to specify basePath

* [eslint] fix linting issue
spalger added a commit that referenced this pull request Nov 22, 2017
* [es][savedObjects/index] put template on each savedObject write

The elasticsearch plugin currently checks for the Kibana index on each iteration of the healthCheck, and creates it if it does not exist. This removes that step from the healthCheck and instead, before each savedObject is written to elasticsearch, ensures that Elasticsearch has the necessary index template should the write result in index creation.

The healthCheck still has the `patchKibanaIndex()` logic, which checks the type in the Kibana index and adds any missing types. This step now does nothing when the Kibana index does not exist, and does what it has always done when it does.

* [ftr] remove unused kibanaIndex service

(cherry picked from commit b1ef897dafeb6d43fe279776e44a9d793a389dc3)

* [savedObjects/integration] create now creates kibana index

* [es/healthCheck] remove use of format()

* [es/healthCheck/tests] use sinon assertions

* [es/patchKibanaIndex] test for kibana index missing behavior

* [savedObjects/errors] add tests for EsAutoCreateIndexError

* [savedObjects/config] deprecate and remove savedObjects.indexCheckTimeout config

* use dangling commas consistently

* [ui/error_auto_create_index] fix class names

* [ui/savedObjectsClient] no need to specify basePath

* [eslint] fix linting issue
@rhoboat
Copy link

rhoboat commented Jan 30, 2018

What about the idea of adding an empty ingest pipeline to .kibana index, to make it future-proof? This could be really cool. We can always help people upgrade their old indices if we had this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v6.1.0 v7.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants