Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Currently, the only way for a command developer to modify cluster configuration is through internal methods or to manually edit the configuration xml file while the cluster is offline.
It can be expected that a module developer will who create commands or functions that require information in or modification of will need to modify the cluster configuration .
Thereforeas well, therefore, we should expose a public API for to retrieve or manipulate the cluster configuration, to promote correctness and ease of use.

 

Background

The cache-1.0.xsd defines the permissible structure of the cluster config, currently represented in XML.
Developers can include their own cluster configuration elements in one of two places as defined in the cache-1.0.xsd: at the cache level or at the region level.
These accept any XML element, so long as the namespace does not match Geode's (i.e., {{http://geode.apache.org/schema/cache}}).

Goals

  • The API is intended to be used by sub-module developers to create their own commands that would need to access/modify the cluster configuration.
  • The API is intended to ONLY update the cluster or server-group's configuration saved by the locator(s). For example, when developer uses the api to add a region definition to the cache configuration, it should NOT be expected that the api will also create the regions for you on the existing servers
  • The API should not be tied with XML. We should be able to change how we save cluster configuration internally without changing the api.
  • Expose a clean Java API for retrieval and modification of the cluster configuration.

Anti-Goals

  • This API is not to be confused with an API that app-developers can use from client or from servers to, say, create a region and update the cluster configuration at the same time. That API would entail a remote invocation of this lower-level api, but it's not the same.

Approach

  • Define a no-op interfaces CacheElement and RegionElement to identify classes that may be saved to the cache and region XML entities, respectively.
  • Leverage JAXB to generate an initial set of configuration objects .  from cache-1.0.xsd. These will be used internally to marshall/unmarshll from/to xml. Developers only need to work with these configuration objects, not directly with xml.
  • Module developers will use their own xsd to create module specific configuration object. The api should allow developers to pass in these objects and be saved in the cluster configuration.
  • Expose a public ClusterConfigurationService via a public abstract command.Expose ClusterConfigurationService via the Locator, which will be exposed via Cache

Proposal

As a minimum viable product, we would require only two methods: a getter and an updater.

No Format
public interface ClusterConfigurationService {
  /**
  * @param group The group to which the cluster configuration applies.
  *        For configuration that applies to every member, use the group default is "cluster".
  * @param additionalBindClass custom Theseelement classes willcreated allowby the returned CacheConfig to include objects
  *        if the bound types, rather than defaulting to {@link com.sun.org.apache.xerces.internal.dom.ElementNSImpl}
  *        to represent the configuration elements XML.other modules if present.
  * @return The cluster configuration for the specified group.
  */
  CacheConfig getCacheConfig(String group, Class<? extends ClassCacheElement>... additionalBindClass);
 
  /**
  * @param group The group to which the cluster configuration applies.
 default *        For configuration that applies to every member, use the group is "cluster".
  * @param mutator Specification of how the cluster configuration should be altered.
  * @param additionalBindClass Thesecustom element classes willcreated allowby the returned CacheConfig to include objects
  *        if the bound types, rather than defaulting to {@link com.sun.org.apache.xerces.internal.dom.ElementNSImpl}
  *        to represent the configuration elements XML.
  *other modules if present.
  */
  void updateCacheConfig(String group, UnaryOperator<CacheConfig> mutator, Class Class<? extends CacheElement>... additionalBindClass);
}

 

...

We accept the UnaryOperator to mutate the existing cluster configuration rather than accepting an explicit CacheConfig to overwrite the existing cluster configuration in the interest of safe concurrent access.  

For additional utility, each method could assume group = "cluster" when not provided.

 

The ClusterConfigurationService will be made available from the Cache, provided the Cache is managed by a Locator.  The service is unavailable for other members.


Intended Use:

Given an interface as the above, we would be able to implement, for instance, the CreateIndexCommand instead as

No Format
public class CreateIndexCommand implements GfshCommand {
  private static final CreateIndexFunction createIndexFunction = new CreateIndexFunction();

  @CliCommand(value = CliStrings.CREATE_INDEX, help = CliStrings.CREATE_INDEX__HELP)
  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
  @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
      operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.QUERY)
  public Result createIndex(...){
 
  Result result;
  final Set<DistributedMember> targetMembers = findMembers(group, memberNameOrID);

  if (targetMembers.isEmpty()) {
    return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
  }

  // Objective: create this Index object.
  // This class was generated by JAXB
  RegionConfig.Index index = new RegionConfig.Index();
  index.setName(indexName);
  index.setExpression(indexedExpression);
  index.setType(indexType.name());
  index.setFromClause(regionPath);

  // Passinvoke the function toon each member, where the index will be instantiated.created
  List<CliFunctionResult> functionResults =
      executeAndGetFunctionResult(createIndexFunction, index, targetMembers);
  result = ResultBuilder.buildResult(functionResults);

  ClusterConfigurationService service = getSharedConfiguration();
  if (result.getStatus().equals(Result.Status.OK) && service != null) {
    // If the function executes successfully and the ClusterConfigurationService exists
    // (i.e., the member executing this command is a Locator), then update the cluster config.

    UnaryOperator<CacheConfig> mutator = 	service.updateCacheConfig("cluster", cc -> {
	      RegionConfig regionConfig =
          cc.getRegion().stream().filter(x -> x.getName().equals(regionPath)).findFirst() findRegionConfig(cc, regionPath);
      if(regionConfig == null) throw new EntityNotFoundException("region is  .orElse(nullnot found");
      regionConfig.getIndex().add(index);
      return cc;
    });
    service.updateCacheConfig("cluster", mutator);
  }
  return result;
}

 

Here, we declaratively build the RegionConfig.Index object we would like to add to the configuration.
On successfull creation, we search for the RegionConfig object to which the index configuration should belong and add this index to that array.
With the mutator defined, we execute (a concurrency-safe) update to the cluster configuration.

Comments

Common operations, such as the above "find region config", could also be considered for initial inclusion to the public API.

...

Custom Cache Element

The above example shows you how you can retrieve/manipuate those elements (e.g. regions, cache servers, gateway receivers, indecies etc) that are defined in CacheConfig (cache.xsd) itself. But cache.xsd allows module developers to put elements of different namespaces inside <cache> and <region> elements, as shown by these methods:

No Format
In CacheConfig: public List<CacheElement> getCustomCacheElements();
In RegionConfig: public List<CacheElement> getCustomRegionElements();

For developers who can easily add/remove these elements from cluster configuration, it is desirable to have these helper methods in the api. These are essentially achieved by calling the two interfaces methods we defined above (getCacheConfig and updateCacheConfig) internally. So they can be provided as default implemetations of the interface.

No Format
// helper methods to access custom elments under <cache>
<T extends CacheElement> List<T> getCustomCacheElements(String group, Class<T> classT);
<T extends CacheElement> T getCustomCacheElement(String group, String elementId, Class<T> classT);
void saveCustomCacheElement(String group, CacheElement element);
void deleteCustomCacheElement(String group, String elementId, Class<? extends CacheElement> classT);

// helper methods to access custom elements under <region>
<T extends CacheElement> List<T> getCustomRegionElements(String group, String regionPath, Class<T> classT);
<T extends CacheElement> T getCustomRegionElement(String group, String regionPath, String elementId, Class<T> classT)
void saveCustomRegionElement(String group, String regionPath, CacheElement element);
void deleteCustomRegionElement(String group, String regionPath, String elementId, Class<? extends CacheElement> classT);


Long-term Goals

  • Reduce duplication of effort by replacing "creation" classes (i.e., CacheCreation, RegionCreation, BindingCreation, et al) with JAXB-generated classes.
  • Remove requirement of JAXB / XML annotations on configuration element classes.
  • This api is meant to be used by commands that would modify the Cache configuration, e.g. create region, create gateway receiver etc. It's only available where these commands are executed. After this, we might expose another set of java API that would be available in other nodes like client or servers. That api would allow developer to create a region as well as modifying the cluster configuration at the same time. 

...