Briefly, this error occurs when Elasticsearch is unable to execute a listener callback during the loading of global ordinals, which are used for faster sorting and aggregations. This could be due to a variety of reasons such as insufficient memory, heavy load, or a bug in the code. To resolve this issue, you could try increasing the heap size, optimizing your queries to reduce load, or updating Elasticsearch to the latest version to fix potential bugs. If the problem persists, consider reviewing your code for any potential issues with the listener implementation.
This guide will help you check for common problems that cause the log ” Failed to call listener on global ordinals loading ” to appear. To understand the issues related to this log, read the explanation below about the following Elasticsearch concepts: indices, cache, fielddata.
Overview
Elasticsearch uses three types of caches to improve the efficiency of operation.
- Node request cache
- Shard data cache
- Field data cache
How they work
Node request cache maintains the results of queries used in a filter context. The results are evicted on a least recently used basis.
Shard data cache maintains the results of frequently used queries where size=0, particularly the results of aggregations. This cache is particularly relevant for logging use cases where data is not updated on old indices, and regular aggregations can be kept in cache to be reused.
The field data cache is used for sorting and aggregations. To keep these operations quick Elasticsearch loads these values into memory.
Examples
Elasticsearch usually manages cache behind the scenes, without the need for any specific settings. However, it is possible to monitor and limit the amount of memory being used on each node for a given cache type by putting the following in elasticsearch.yml :
indices.queries.cache.size: 10% indices.fielddata.cache.size: 30%
Note, the above values are in fact the defaults, and there is no need to set them specifically. The default values are good for most use cases, and should rarely be modified.
You can monitor the use of caches on each node like this:
GET /_nodes/stats/indices/fielddata GET /_nodes/stats/indices/query_cache GET /_nodes/stats/indices/request_cache
Notes and good things to know
Construct your queries with reusable filters. There are certain parts of your query which are good candidates to be reused across a large number of queries, and you should design your queries with this in mind. Anything thing that does not need to be scored should go in the filter section of a bool query. For example, time ranges, language selectors, or clauses that exclude inactive documents are all likely to be excluded in a large number of queries, and should be included in filter parts of the query so that they can be cached and reused.
In particular, take care with time filters. “now-15m” cannot be reused, because “now” will continually change as the time window moves on. On the other hand “now-15/m” will round to the nearest minute, and can be re-used (via cache) for 60 seconds before rolling over to the next minute.
For example when a user enters the search term “brexit”, we may want to also filter on language and time period to return relevant articles. The query below leaves only the query term “brexit” in the “must” part of the query, because this is the only part which should affect the relevance score. The time filter and language filter can be reused time and time again for new queries for different searches.
POST results/_search { "query": { "bool": { "must": [ { "match": { "message": { "query": "brexit" } } } ], "filter": [ { "range": { "@timestamp": { "gte": "now-10d/d" } } }, { "term": { "lang.keyword": { "value": "en", "boost": 1 } } } ] } } }
Limit the use of field data. Be careful about using fielddata=true in your mapping where the number of terms will result in a high cardinality. If you must use fielddata=true, you can also reduce the requirement of fielddata cache by limiting the requirements for fielddata for a given index using a field data frequency filter.
POST results/_search { "query": { "bool": { "must": [ { "match": { "message": { "query": "brexit" } } } ], "filter": [ { "range": { "@timestamp": { "gte": "now-10d/d" } } }, { "term": { "lang.keyword": { "value": "en", "boost": 1 } } } ] } } }
Overview
In Elasticsearch the term fielddata is relevant when sorting and doing aggregations (similar to SQL GROUP BY COUNT and AVERAGE functions) on text fields.
For performance reasons, there are some rules as to the kinds of fields that can be aggregated. You can group by any numeric field but for text fields, which have to be of keyword type or have fielddata=true since they don’t support doc_values (Doc values are the on-disk inverted index data structure, built at document indexing time, which makes aggregations possible).
Fielddata is an in-memory data structure used by text fields for the same purpose. Since it uses a lot of heap size it is disabled by default.
Examples
The following PUT mapping API call will enable Fielddata on my_field text field.
PUT my_index/_mapping{"properties":{"my_field":{"type":"text","fielddata":true}}}
Notes
- As field-data is disabled by default on text fields, in case of an attempt to aggregate on a text field with field-data disabled, you would get the following error message:
“Fielddata is disabled on text fields by default. Set `fielddata=true` on [`your_field_name`] in order to load field data in memory by uninverting the inverted index. Note that this can however, use “significant memory.” – if this happens you can either enable the field-data on that text field, or choose another way to query the data (again, because field-data consumes a lot of memory and is not recommended).
Log Context
Log “Failed to call listener on global ordinals loading” classname is IndicesFieldDataCache.java.
We extracted the following from Elasticsearch source code for those seeking an in-depth context :
if (ifd instanceof GlobalOrdinalsAccounting) { listener.onCache(shardId; fieldName; (GlobalOrdinalsAccounting) ifd); } } catch (Exception e) { // load anyway since listeners should not throw exceptions logger.error("Failed to call listener on global ordinals loading"; e); } } return (Accountable) ifd; }); return (IFD) accountable;