Skip to content

Commit

Permalink
Multi-instance Support (#311)
Browse files Browse the repository at this point in the history
  • Loading branch information
lbwexler authored Apr 5, 2024
1 parent 8166927 commit e9fa030
Show file tree
Hide file tree
Showing 72 changed files with 2,260 additions and 625 deletions.
67 changes: 67 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,73 @@

## 20.0-SNAPSHOT - unreleased

### 🎁 New Features

* Hoist Core v20 provides support for running multi-instance clusters of Hoist application servers.
Cluster management is provided by the use of Hazelcast (www.hazelcast.com), an open-source library
providing embedded java support for inter-server communication, co-ordination, and data sharing.
See the new `ClusterService.groovy` service, which provides the clustering implementation and main
API entry point for accessing the cluster.
** Applications/client plugins upgrading to v18 will need to provide a cluster configuration class
with the name `ClusterConfig.groovy`. See toolbox for an example of this file.
** Applications should fix their Hazelcast version with the following line in their gradle.properties:
`hazelcast.version=5.3.6`
** Applications that intend to run with more than one server *must* enable sticky sessions when
routing clients to servers. This is critical for the correct operation of authentication
and web socket communications.
** Many applications will *not* need to implement additional changes beyond the above to
run with multi-instances; Hoist will setup the cluster, elect a master instance, provide
cluster-aware hibernate caching and logging, and ensure cross-server consistency for its own
APIs.
** However, complex applications -- especially applications with state, workflow, or business
logic -- should take care to ensure the app is safe to run in multi-instance mode. Distributed
data structures (e.g. Hazelcast Maps) should be used as needed, as well as limiting certain
actions to the "master" server. See toolbox, or Hoist for help.
** `hoist-react >= 64.0` is required.
* New support for reporting of service statistics for trobuleshooting/monitoring. Implement
`BaseService.getAdminStats()` to provide diagnostic metadata about the state of your service that
will then be displayed in the admin client.
* All `Throwable`s are now serialized to JSON by default using Hoist's standard customization of
Jackson.

### Breaking Changes
* The following server-side Hoist events are now implemented as cluster-wide Hazelcast messages
rather than single-server Grails events:
** 'xhFeedbackReceived', 'xhClientErrorReceived', 'xhConfigChanged', and 'xhMonitorStatusReport'
Any applications that are listening to these events with `BaseService.subscribe` should instead use
the new cluster aware method `BaseService.subscribeToTopic`.
* The `exceptionRenderer` singleton has been simplified and renamed as `xhExceptionHandler`. This
change was needed to better support cross-cluster exception handling. This object is used by
Hoist internally for catching uncaught exceptions and this change is not expected to impact
most applications.

* New support for Role Management.
* Hoist now supports an out-of-the-box, database-driven system for maintaining a hierarchical
set of roles and associating them with individual users.
* New system supports app and plug-in specific integrations to AD and other enterprise systems.
* Hoist-react `v64` is now required and will provide an administrative UI to visualize and
manage the new role system.
* See `DefaultRoleService` for more information.

### ⚙️ Technical

* Add `xh/echoHeaders` utility endpoint. Useful for verifying headers (e.g. `jespa_connection_id`)
that are installed by or must pass through multiple ingresses/load balancers.
* Remove HTML tag escaping when parsing alert banner create/update request JSON.

### 💥 Breaking Changes

* Applications will typically need to adjust their implementation of `BaseRoleService`. Most
applications are expected to adopt the new provided `DefaultRoleService`, and may be required to
migrate existing code/data to the new API. Applications that wish to continue to use a completely
custom `BaseRoleService` will need to implement one additional method: `getUsersForRole`.

### 📚 Libraries
* grails `6.1.0`
* gorm `8.0.1`
* hazelcast `5.3.6`


## 19.0.0 - 2024-04-04

### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW - latest Hoist React + DB col additions)
Expand Down
13 changes: 9 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,17 @@ dependencies {
//--------------------
// Hoist Additions
//--------------------

api "com.hazelcast:hazelcast"

api "org.apache.tomcat:tomcat-jdbc"
api "org.codehaus.groovy:groovy-dateutil:$groovyVersion"

// Quartz, with Grails 4/5 workaround (https://github.com/grails-plugins/grails-quartz/issues/107
api("org.quartz-scheduler:quartz:2.3.2") {exclude group: 'slf4j-api', module: 'c3p0'}
api 'org.grails.plugins:quartz:2.0.13'

api "org.hibernate:hibernate-core:5.6.11.Final"
api "org.hibernate:hibernate-ehcache:5.6.11.Final"
api "net.sf.ehcache:ehcache:2.10.9.2"
api "org.hibernate:hibernate-jcache"

api "org.apache.poi:poi:4.1.2"
api "org.apache.poi:poi-ooxml:4.1.2"
Expand All @@ -72,10 +73,14 @@ dependencies {
api "org.apache.directory.api:api-all:2.1.3"

api "org.springframework:spring-websocket"
api "org.apache.httpcomponents.client5:httpclient5:5.1.2"
api "org.apache.httpcomponents.client5:httpclient5:5.2.1"
api 'org.jasypt:jasypt:1.9.3'
api "commons-io:commons-io:2.8.0"
api "org.owasp.encoder:encoder:1.2.3"

api "com.esotericsoftware:kryo:5.5.0"
api "info.jerrinot:subzero-core:0.11"

}


Expand Down
9 changes: 5 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
xhReleaseVersion=20.0-SNAPSHOT

grailsVersion=6.0.0
grailsGradlePluginVersion=6.0.0
grailsHibernatePluginVersion=8.0.0
grailsVersion=6.1.2
grailsGradlePluginVersion=6.1.2
grailsHibernatePluginVersion=8.1.0
groovyVersion=3.0.11
gormVersion=8.0.0
gormVersion=8.1.0
logback.version=1.2.7
hazelcast.version=5.3.6

org.gradle.daemon=true
org.gradle.parallel=true
Expand Down
17 changes: 13 additions & 4 deletions grails-app/controllers/io/xh/hoist/BaseController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ package io.xh.hoist
import grails.async.Promise
import grails.async.web.WebPromises
import groovy.transform.CompileStatic
import io.xh.hoist.exception.ExceptionRenderer
import io.xh.hoist.exception.ExceptionHandler
import io.xh.hoist.json.JSONParser
import io.xh.hoist.json.JSONSerializer
import io.xh.hoist.log.LogSupport
Expand All @@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory
abstract class BaseController implements LogSupport, IdentitySupport {

IdentityService identityService
ExceptionRenderer exceptionRenderer
ExceptionHandler xhExceptionHandler

/**
* Render an object to JSON.
Expand Down Expand Up @@ -82,7 +82,7 @@ abstract class BaseController implements LogSupport, IdentitySupport {
WebPromises.task {
c.call()
}.onError { Throwable t ->
exceptionRenderer.handleException(t, request, response, this)
handleUncaughtInternal(t)
}
}

Expand All @@ -95,7 +95,16 @@ abstract class BaseController implements LogSupport, IdentitySupport {
// Implementation
//-------------------
void handleException(Exception ex) {
exceptionRenderer.handleException(ex, request, response, this)
handleUncaughtInternal(ex)
}

private void handleUncaughtInternal(Throwable t) {
xhExceptionHandler.handleException(
exception: t,
logTo: this,
logMessage: [_action: actionName],
renderTo: response
)
}

// Provide cached logger to LogSupport for possible performance benefit
Expand Down

This file was deleted.

This file was deleted.

26 changes: 0 additions & 26 deletions grails-app/controllers/io/xh/hoist/admin/EnvAdminController.groovy

This file was deleted.

Loading

0 comments on commit e9fa030

Please sign in to comment.