| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.solr.cloud.autoscaling; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| import org.apache.lucene.store.AlreadyClosedException; |
| import org.apache.solr.client.solrj.cloud.SolrCloudManager; |
| import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventType; |
| import org.apache.solr.common.util.Utils; |
| import org.apache.solr.core.SolrResourceLoader; |
| |
| /** |
| * Common autoscaling interfaces. |
| * |
| * @deprecated to be removed in Solr 9.0 (see SOLR-14656) |
| */ |
| public class AutoScaling { |
| |
| /** |
| * Implementation of this interface is used for processing events generated by a trigger. |
| */ |
| public interface TriggerEventProcessor { |
| |
| /** |
| * This method is executed for events produced by {@link Trigger#run()}. |
| * |
| * @param event a subclass of {@link TriggerEvent} |
| * @return true if the processor was ready to perform actions on the event, false |
| * otherwise. If false was returned then callers should assume the event was discarded. |
| */ |
| boolean process(TriggerEvent event); |
| } |
| |
| /** |
| * Interface for a Solr trigger. Each trigger implements Runnable and Closeable interface. A trigger |
| * is scheduled using a {@link java.util.concurrent.ScheduledExecutorService} so it is executed as |
| * per a configured schedule to check whether the trigger is ready to fire. The {@link AutoScaling.Trigger#setProcessor(AutoScaling.TriggerEventProcessor)} |
| * method should be used to set a processor which is used by implementation of this class whenever |
| * ready. |
| * <p> |
| * As per the guarantees made by the {@link java.util.concurrent.ScheduledExecutorService} a trigger |
| * implementation is only ever called sequentially and therefore need not be thread safe. However, it |
| * is encouraged that implementations be immutable with the exception of the associated listener |
| * which can be get/set by a different thread than the one executing the trigger. Therefore, implementations |
| * should use appropriate synchronization around the listener. |
| * <p> |
| * When a trigger is ready to fire, it calls the {@link TriggerEventProcessor#process(TriggerEvent)} event |
| * with the proper trigger event object. If that method returns false then it should be interpreted to mean |
| * that Solr is not ready to process this trigger event and therefore we should retain the state and fire |
| * at the next invocation of the run() method. |
| */ |
| public interface Trigger extends Closeable, Runnable { |
| /** |
| * Trigger name. |
| */ |
| String getName(); |
| |
| /** |
| * Event type generated by this trigger. |
| */ |
| TriggerEventType getEventType(); |
| |
| /** Returns true if this trigger is enabled. */ |
| boolean isEnabled(); |
| |
| /** Trigger properties. */ |
| Map<String, Object> getProperties(); |
| |
| /** Number of seconds to wait between fired events ("waitFor" property). */ |
| int getWaitForSecond(); |
| |
| /** Actions to execute when event is fired. */ |
| List<TriggerAction> getActions(); |
| |
| /** Set event processor to call when event is fired. */ |
| void setProcessor(TriggerEventProcessor processor); |
| |
| /** Get event processor. */ |
| TriggerEventProcessor getProcessor(); |
| |
| /** Return true when this trigger is closed and cannot be used. */ |
| boolean isClosed(); |
| |
| /** Set internal state of this trigger from another instance. */ |
| void restoreState(Trigger old); |
| |
| /** Save internal state of this trigger in ZooKeeper. */ |
| void saveState(); |
| |
| /** Restore internal state of this trigger from ZooKeeper. */ |
| void restoreState(); |
| |
| /** |
| * Called when trigger is created but before it's initialized or scheduled for use. |
| * This method should also verify that the trigger configuration parameters are correct. It may |
| * be called multiple times. |
| * @param properties configuration properties |
| * @throws TriggerValidationException contains details of invalid configuration parameters. |
| */ |
| void configure(SolrResourceLoader loader, SolrCloudManager cloudManager, Map<String, Object> properties) throws TriggerValidationException; |
| |
| /** |
| * Called before a trigger is scheduled. Any heavy object creation or initialisation should |
| * be done in this method instead of the Trigger's constructor. |
| */ |
| void init() throws Exception; |
| } |
| |
| /** |
| * Factory to produce instances of {@link Trigger}. |
| */ |
| public static abstract class TriggerFactory implements Closeable { |
| protected boolean isClosed = false; |
| |
| public abstract Trigger create(TriggerEventType type, String name, Map<String, Object> props) throws TriggerValidationException; |
| |
| @Override |
| public void close() throws IOException { |
| synchronized (this) { |
| isClosed = true; |
| } |
| } |
| } |
| |
| /** |
| * Default implementation of {@link TriggerFactory}. |
| */ |
| public static class TriggerFactoryImpl extends TriggerFactory { |
| |
| private final SolrCloudManager cloudManager; |
| private final SolrResourceLoader loader; |
| |
| public TriggerFactoryImpl(SolrResourceLoader loader, SolrCloudManager cloudManager) { |
| Objects.requireNonNull(cloudManager); |
| Objects.requireNonNull(loader); |
| this.cloudManager = cloudManager; |
| this.loader = loader; |
| } |
| |
| @Override |
| public synchronized Trigger create(TriggerEventType type, String name, Map<String, Object> props) throws TriggerValidationException { |
| if (isClosed) { |
| throw new AlreadyClosedException("TriggerFactory has already been closed, cannot create new triggers"); |
| } |
| if (type == null) { |
| throw new IllegalArgumentException("Trigger type must not be null"); |
| } |
| if (name == null || name.isEmpty()) { |
| throw new IllegalArgumentException("Trigger name must not be empty"); |
| } |
| Trigger t; |
| switch (type) { |
| case NODEADDED: |
| t = new NodeAddedTrigger(name); |
| break; |
| case NODELOST: |
| t = new NodeLostTrigger(name); |
| break; |
| case SEARCHRATE: |
| t = new SearchRateTrigger(name); |
| break; |
| case METRIC: |
| t = new MetricTrigger(name); |
| break; |
| case SCHEDULED: |
| t = new ScheduledTrigger(name); |
| break; |
| case INDEXSIZE: |
| t = new IndexSizeTrigger(name); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown event type: " + type + " in trigger: " + name); |
| } |
| t.configure(loader, cloudManager, props); |
| return t; |
| } |
| |
| } |
| |
| public static final String AUTO_ADD_REPLICAS_TRIGGER_NAME = ".auto_add_replicas"; |
| |
| public static final String AUTO_ADD_REPLICAS_TRIGGER_DSL = |
| " {" + |
| " 'name' : '" + AUTO_ADD_REPLICAS_TRIGGER_NAME + "'," + |
| " 'event' : 'nodeLost'," + |
| " 'waitFor' : -1," + |
| " 'enabled' : true," + |
| " 'actions' : [" + |
| " {" + |
| " 'name':'auto_add_replicas_plan'," + |
| " 'class':'solr.AutoAddReplicasPlanAction'" + |
| " }," + |
| " {" + |
| " 'name':'execute_plan'," + |
| " 'class':'solr.ExecutePlanAction'" + |
| " }" + |
| " ]" + |
| " }"; |
| |
| @SuppressWarnings({"unchecked"}) |
| public static final Map<String, Object> AUTO_ADD_REPLICAS_TRIGGER_PROPS = (Map) Utils.fromJSONString(AUTO_ADD_REPLICAS_TRIGGER_DSL); |
| |
| public static final String SCHEDULED_MAINTENANCE_TRIGGER_NAME = ".scheduled_maintenance"; |
| |
| public static final String SCHEDULED_MAINTENANCE_TRIGGER_DSL = |
| " {" + |
| " 'name' : '" + SCHEDULED_MAINTENANCE_TRIGGER_NAME + "'," + |
| " 'event' : 'scheduled'," + |
| " 'startTime' : 'NOW'," + |
| " 'every' : '+1DAY'," + |
| " 'enabled' : true," + |
| " 'actions' : [" + |
| " {" + |
| " 'name':'inactive_shard_plan'," + |
| " 'class':'solr.InactiveShardPlanAction'" + |
| " }," + |
| " {" + |
| " 'name':'inactive_markers_plan'," + |
| " 'class':'solr.InactiveMarkersPlanAction'" + |
| " }," + |
| " {" + |
| " 'name':'execute_plan'," + |
| " 'class':'solr.ExecutePlanAction'" + |
| " }" + |
| " ]" + |
| " }"; |
| |
| @SuppressWarnings({"unchecked"}) |
| public static final Map<String, Object> SCHEDULED_MAINTENANCE_TRIGGER_PROPS = (Map) Utils.fromJSONString(SCHEDULED_MAINTENANCE_TRIGGER_DSL); |
| |
| } |