PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/org/elasticsearch/indices/memory/IndexingMemoryController.java

https://bitbucket.org/paulscott56/elasticsearch
Java | 271 lines | 205 code | 33 blank | 33 comment | 42 complexity | 694cf778664399e6b79f9c96dc0d9b87 MD5 | raw file
  1. /*
  2. * Licensed to ElasticSearch and Shay Banon under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. ElasticSearch licenses this
  6. * file to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.indices.memory;
  20. import com.google.common.collect.Lists;
  21. import com.google.common.collect.Maps;
  22. import org.elasticsearch.ElasticSearchException;
  23. import org.elasticsearch.common.component.AbstractLifecycleComponent;
  24. import org.elasticsearch.common.inject.Inject;
  25. import org.elasticsearch.common.settings.Settings;
  26. import org.elasticsearch.common.unit.ByteSizeUnit;
  27. import org.elasticsearch.common.unit.ByteSizeValue;
  28. import org.elasticsearch.common.unit.TimeValue;
  29. import org.elasticsearch.index.engine.Engine;
  30. import org.elasticsearch.index.engine.EngineClosedException;
  31. import org.elasticsearch.index.engine.FlushNotAllowedEngineException;
  32. import org.elasticsearch.index.service.IndexService;
  33. import org.elasticsearch.index.shard.ShardId;
  34. import org.elasticsearch.index.shard.service.IndexShard;
  35. import org.elasticsearch.index.shard.service.InternalIndexShard;
  36. import org.elasticsearch.index.translog.Translog;
  37. import org.elasticsearch.indices.IndicesLifecycle;
  38. import org.elasticsearch.indices.IndicesService;
  39. import org.elasticsearch.monitor.jvm.JvmInfo;
  40. import org.elasticsearch.threadpool.ThreadPool;
  41. import java.util.List;
  42. import java.util.Map;
  43. import java.util.concurrent.ScheduledFuture;
  44. /**
  45. *
  46. */
  47. public class IndexingMemoryController extends AbstractLifecycleComponent<IndexingMemoryController> {
  48. private final ThreadPool threadPool;
  49. private final IndicesService indicesService;
  50. private final ByteSizeValue indexingBuffer;
  51. private final ByteSizeValue minShardIndexBufferSize;
  52. private final ByteSizeValue maxShardIndexBufferSize;
  53. private final TimeValue inactiveTime;
  54. private final TimeValue interval;
  55. private final Listener listener = new Listener();
  56. private final Map<ShardId, ShardIndexingStatus> shardsIndicesStatus = Maps.newHashMap();
  57. private volatile ScheduledFuture scheduler;
  58. private final Object mutex = new Object();
  59. @Inject
  60. public IndexingMemoryController(Settings settings, ThreadPool threadPool, IndicesService indicesService) {
  61. super(settings);
  62. this.threadPool = threadPool;
  63. this.indicesService = indicesService;
  64. ByteSizeValue indexingBuffer;
  65. String indexingBufferSetting = componentSettings.get("index_buffer_size", "10%");
  66. if (indexingBufferSetting.endsWith("%")) {
  67. double percent = Double.parseDouble(indexingBufferSetting.substring(0, indexingBufferSetting.length() - 1));
  68. indexingBuffer = new ByteSizeValue((long) (((double) JvmInfo.jvmInfo().mem().heapMax().bytes()) * (percent / 100)));
  69. ByteSizeValue minIndexingBuffer = componentSettings.getAsBytesSize("min_index_buffer_size", new ByteSizeValue(48, ByteSizeUnit.MB));
  70. ByteSizeValue maxIndexingBuffer = componentSettings.getAsBytesSize("max_index_buffer_size", null);
  71. if (indexingBuffer.bytes() < minIndexingBuffer.bytes()) {
  72. indexingBuffer = minIndexingBuffer;
  73. }
  74. if (maxIndexingBuffer != null && indexingBuffer.bytes() > maxIndexingBuffer.bytes()) {
  75. indexingBuffer = maxIndexingBuffer;
  76. }
  77. } else {
  78. indexingBuffer = ByteSizeValue.parseBytesSizeValue(indexingBufferSetting, null);
  79. }
  80. this.indexingBuffer = indexingBuffer;
  81. this.minShardIndexBufferSize = componentSettings.getAsBytesSize("min_shard_index_buffer_size", new ByteSizeValue(4, ByteSizeUnit.MB));
  82. // LUCENE MONITOR: Based on this thread, currently (based on Mike), having a large buffer does not make a lot of sense: https://issues.apache.org/jira/browse/LUCENE-2324?focusedCommentId=13005155&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13005155
  83. this.maxShardIndexBufferSize = componentSettings.getAsBytesSize("max_shard_index_buffer_size", new ByteSizeValue(512, ByteSizeUnit.MB));
  84. this.inactiveTime = componentSettings.getAsTime("shard_inactive_time", TimeValue.timeValueMinutes(30));
  85. // we need to have this relatively small to move a shard from inactive to active fast (enough)
  86. this.interval = componentSettings.getAsTime("interval", TimeValue.timeValueSeconds(30));
  87. logger.debug("using index_buffer_size [{}], with min_shard_index_buffer_size [{}], max_shard_index_buffer_size [{}], shard_inactive_time [{}]", this.indexingBuffer, this.minShardIndexBufferSize, this.maxShardIndexBufferSize, this.inactiveTime);
  88. }
  89. @Override
  90. protected void doStart() throws ElasticSearchException {
  91. indicesService.indicesLifecycle().addListener(listener);
  92. // its fine to run it on the scheduler thread, no busy work
  93. this.scheduler = threadPool.scheduleWithFixedDelay(new ShardsIndicesStatusChecker(), interval);
  94. }
  95. @Override
  96. protected void doStop() throws ElasticSearchException {
  97. indicesService.indicesLifecycle().removeListener(listener);
  98. if (scheduler != null) {
  99. scheduler.cancel(false);
  100. scheduler = null;
  101. }
  102. }
  103. @Override
  104. protected void doClose() throws ElasticSearchException {
  105. }
  106. class ShardsIndicesStatusChecker implements Runnable {
  107. @Override
  108. public void run() {
  109. synchronized (mutex) {
  110. boolean activeInactiveStatusChanges = false;
  111. List<IndexShard> activeToInactiveIndexingShards = Lists.newArrayList();
  112. List<IndexShard> inactiveToActiveIndexingShards = Lists.newArrayList();
  113. for (IndexService indexService : indicesService) {
  114. for (IndexShard indexShard : indexService) {
  115. long time = threadPool.estimatedTimeInMillis();
  116. Translog translog = ((InternalIndexShard) indexShard).translog();
  117. ShardIndexingStatus status = shardsIndicesStatus.get(indexShard.shardId());
  118. if (status == null) { // not added yet
  119. continue;
  120. }
  121. // check if it is deemed to be inactive (sam translogId and numberOfOperations over a long period of time)
  122. if (status.translogId == translog.currentId() && translog.estimatedNumberOfOperations() == 0) {
  123. if (status.time == -1) { // first time
  124. status.time = time;
  125. }
  126. // inactive?
  127. if (!status.inactiveIndexing) {
  128. // mark it as inactive only if enough time has passed and there are no ongoing merges going on...
  129. if ((time - status.time) > inactiveTime.millis() && indexShard.mergeStats().current() == 0) {
  130. // inactive for this amount of time, mark it
  131. activeToInactiveIndexingShards.add(indexShard);
  132. status.inactiveIndexing = true;
  133. activeInactiveStatusChanges = true;
  134. logger.debug("marking shard [{}][{}] as inactive (inactive_time[{}]) indexing wise, setting size to [{}]", indexShard.shardId().index().name(), indexShard.shardId().id(), inactiveTime, Engine.INACTIVE_SHARD_INDEXING_BUFFER);
  135. }
  136. }
  137. } else {
  138. if (status.inactiveIndexing) {
  139. inactiveToActiveIndexingShards.add(indexShard);
  140. status.inactiveIndexing = false;
  141. activeInactiveStatusChanges = true;
  142. logger.debug("marking shard [{}][{}] as active indexing wise", indexShard.shardId().index().name(), indexShard.shardId().id());
  143. }
  144. status.time = -1;
  145. }
  146. status.translogId = translog.currentId();
  147. status.translogNumberOfOperations = translog.estimatedNumberOfOperations();
  148. }
  149. }
  150. for (IndexShard indexShard : activeToInactiveIndexingShards) {
  151. // update inactive indexing buffer size
  152. try {
  153. ((InternalIndexShard) indexShard).engine().updateIndexingBufferSize(Engine.INACTIVE_SHARD_INDEXING_BUFFER);
  154. } catch (EngineClosedException e) {
  155. // ignore
  156. } catch (FlushNotAllowedEngineException e) {
  157. // ignore
  158. }
  159. }
  160. if (activeInactiveStatusChanges) {
  161. calcAndSetShardIndexingBuffer("shards became active/inactive (indexing wise)");
  162. }
  163. }
  164. }
  165. }
  166. class Listener extends IndicesLifecycle.Listener {
  167. @Override
  168. public void afterIndexShardCreated(IndexShard indexShard) {
  169. synchronized (mutex) {
  170. calcAndSetShardIndexingBuffer("created_shard[" + indexShard.shardId().index().name() + "][" + indexShard.shardId().id() + "]");
  171. shardsIndicesStatus.put(indexShard.shardId(), new ShardIndexingStatus());
  172. }
  173. }
  174. @Override
  175. public void afterIndexShardClosed(ShardId shardId, boolean delete) {
  176. synchronized (mutex) {
  177. calcAndSetShardIndexingBuffer("removed_shard[" + shardId.index().name() + "][" + shardId.id() + "]");
  178. shardsIndicesStatus.remove(shardId);
  179. }
  180. }
  181. }
  182. private void calcAndSetShardIndexingBuffer(String reason) {
  183. int shardsCount = countShards();
  184. if (shardsCount == 0) {
  185. return;
  186. }
  187. ByteSizeValue shardIndexingBufferSize = calcShardIndexingBuffer(shardsCount);
  188. if (shardIndexingBufferSize == null) {
  189. return;
  190. }
  191. if (shardIndexingBufferSize.bytes() < minShardIndexBufferSize.bytes()) {
  192. shardIndexingBufferSize = minShardIndexBufferSize;
  193. }
  194. if (shardIndexingBufferSize.bytes() > maxShardIndexBufferSize.bytes()) {
  195. shardIndexingBufferSize = maxShardIndexBufferSize;
  196. }
  197. logger.debug("recalculating shard indexing buffer (reason={}), total is [{}] with [{}] active shards, each shard set to [{}]", reason, indexingBuffer, shardsCount, shardIndexingBufferSize);
  198. for (IndexService indexService : indicesService) {
  199. for (IndexShard indexShard : indexService) {
  200. ShardIndexingStatus status = shardsIndicesStatus.get(indexShard.shardId());
  201. if (status == null || !status.inactiveIndexing) {
  202. try {
  203. ((InternalIndexShard) indexShard).engine().updateIndexingBufferSize(shardIndexingBufferSize);
  204. } catch (EngineClosedException e) {
  205. // ignore
  206. continue;
  207. } catch (FlushNotAllowedEngineException e) {
  208. // ignore
  209. continue;
  210. } catch (Exception e) {
  211. logger.warn("failed to set shard [{}][{}] index buffer to [{}]", indexShard.shardId().index().name(), indexShard.shardId().id(), shardIndexingBufferSize);
  212. }
  213. }
  214. }
  215. }
  216. }
  217. private ByteSizeValue calcShardIndexingBuffer(int shardsCount) {
  218. return new ByteSizeValue(indexingBuffer.bytes() / shardsCount);
  219. }
  220. private int countShards() {
  221. int shardsCount = 0;
  222. for (IndexService indexService : indicesService) {
  223. for (IndexShard indexShard : indexService) {
  224. ShardIndexingStatus status = shardsIndicesStatus.get(indexShard.shardId());
  225. if (status == null || !status.inactiveIndexing) {
  226. shardsCount++;
  227. }
  228. }
  229. }
  230. return shardsCount;
  231. }
  232. static class ShardIndexingStatus {
  233. long translogId = -1;
  234. int translogNumberOfOperations = -1;
  235. boolean inactiveIndexing = false;
  236. long time = -1; // contains the first time we saw this shard with no operations done on it
  237. }
  238. }