Skip to content

Commit

Permalink
Support remote Json Serialization in runOnInstance (#424)
Browse files Browse the repository at this point in the history
  • Loading branch information
lbwexler authored Nov 29, 2024
1 parent 8d4a14b commit a4a64f9
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 64 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## 26.0-SNAPSHOT - unreleased

### 💥 Breaking Changes (upgrade difficulty: 🟢 TRIVIAL - change to runOnInstance signature.)

### 🎁 New Features
* `BaseController.runOnInstance` now performs the json serialization on the target instance. This
allows lighter-weight remote endpoint executions, that do not require object serialization.
Applications must now provide a `ClusterJsonRequest` to this method rather than a `ClusterRequest`

### ⚙️ Technical
* Align all-built in log names to have form "App-Cluster-xxx.log"

Expand Down
17 changes: 8 additions & 9 deletions grails-app/controllers/io/xh/hoist/BaseController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package io.xh.hoist

import grails.async.Promise
import groovy.transform.CompileStatic
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.cluster.ClusterJsonResponse
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterService
import io.xh.hoist.exception.ExceptionHandler
Expand Down Expand Up @@ -88,18 +90,15 @@ abstract class BaseController implements LogSupport, IdentitySupport {
* Note: Any exception that occurs is assumed to be logged on the target instance
* and will not be logged on the instance running this action.
*/
protected void runOnInstance(ClusterRequest task, String instance) {
def ret = clusterService.submitToInstance(task, instance)
if (ret.exception) {
// Just render exception, was already logged on target instance
xhExceptionHandler.handleException(exception: ret.exception, renderTo: response)
return
}
renderJSON(ret.value)
protected void runOnInstance(ClusterJsonRequest task, String instance) {
ClusterJsonResponse ret = clusterService.submitToInstance(task, instance)
response.setStatus(ret.httpStatus)
response.setContentType('application/json; charset=UTF-8')
render(ret.json)
}

/** Run a task on the primary cluster instance. */
protected void runOnPrimary(ClusterRequest task) {
protected void runOnPrimary(ClusterJsonRequest task) {
runOnInstance(task, clusterService.primaryName)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
package io.xh.hoist.admin

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access
import static io.xh.hoist.util.Utils.getAppContext

Expand All @@ -25,9 +25,10 @@ class MonitorResultsAdminController extends BaseController {
def forceRunAllMonitors() {
runOnPrimary(new ForceRunAllMonitors())
}
static class ForceRunAllMonitors extends ClusterRequest {
static class ForceRunAllMonitors extends ClusterJsonRequest {
def doCall() {
appContext.monitorService.forceRun()
[success: true]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access
import io.xh.hoist.util.Utils

Expand Down Expand Up @@ -42,7 +42,7 @@ class ClusterAdminController extends BaseController {
// Wait enough to let async call below complete
sleep(5 * SECONDS)
}
static class ShutdownInstance extends ClusterRequest {
static class ShutdownInstance extends ClusterJsonRequest {
def doCall() {
// Run async to allow this call to successfully return.
task {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access

import static io.xh.hoist.util.Utils.appContext
Expand All @@ -18,7 +18,7 @@ class ConnectionPoolMonitorAdminController extends BaseController {
def snapshots(String instance) {
runOnInstance(new Snapshots(), instance)
}
static class Snapshots extends ClusterRequest {
static class Snapshots extends ClusterJsonRequest {
def doCall() {
def svc = appContext.connectionPoolMonitoringService
return [
Expand All @@ -34,7 +34,7 @@ class ConnectionPoolMonitorAdminController extends BaseController {
def takeSnapshot(String instance) {
runOnInstance(new TakeSnapshot(), instance)
}
static class TakeSnapshot extends ClusterRequest {
static class TakeSnapshot extends ClusterJsonRequest {
def doCall() {
appContext.connectionPoolMonitoringService.takeSnapshot()
}
Expand All @@ -44,7 +44,7 @@ class ConnectionPoolMonitorAdminController extends BaseController {
def resetStats() {
runOnInstance(new ResetStats())
}
static class ResetStats extends ClusterRequest {
static class ResetStats extends ClusterJsonRequest {
def doCall() {
appContext.connectionPoolMonitoringService.resetStats()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access

import static io.xh.hoist.util.Utils.isSensitiveParamName
Expand All @@ -19,7 +19,7 @@ class EnvAdminController extends BaseController {
def index(String instance) {
runOnInstance(new Index(), instance)
}
static class Index extends ClusterRequest {
static class Index extends ClusterJsonRequest {
def doCall() {
[
environment: System.getenv().collectEntries {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access

import static io.xh.hoist.util.Utils.appContext
Expand All @@ -20,7 +20,7 @@ class HzObjectAdminController extends BaseController {
runOnInstance(new ListObjects(), instance)
}

static class ListObjects extends ClusterRequest {
static class ListObjects extends ClusterJsonRequest {
def doCall() {
appContext.clusterAdminService.listObjects()
}
Expand All @@ -31,7 +31,7 @@ class HzObjectAdminController extends BaseController {
runOnInstance(new ClearObjects(names: params.list('names')), instance)
}

static class ClearObjects extends ClusterRequest {
static class ClearObjects extends ClusterJsonRequest {
List<String> names

def doCall() {
Expand All @@ -45,7 +45,7 @@ class HzObjectAdminController extends BaseController {
runOnInstance(new ClearHibernateCaches(), instance)
}

static class ClearHibernateCaches extends ClusterRequest {
static class ClearHibernateCaches extends ClusterJsonRequest {
def doCall() {
appContext.clusterAdminService.clearHibernateCaches()
return [success: true]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package io.xh.hoist.admin.cluster
import groovy.io.FileType
import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.configuration.LogbackConfig
import io.xh.hoist.security.Access
import io.xh.hoist.exception.RoutineRuntimeException
Expand All @@ -23,7 +24,7 @@ class LogViewerAdminController extends BaseController {
runOnInstance(new ListFiles(), instance)
}

static class ListFiles extends ClusterRequest {
static class ListFiles extends ClusterJsonRequest {
def doCall() {
def logRootPath = appContext.logReaderService.logDir.absolutePath,
files = availableFiles.collect {
Expand Down Expand Up @@ -58,7 +59,7 @@ class LogViewerAdminController extends BaseController {
)
}

static class GetFile extends ClusterRequest {
static class GetFile extends ClusterJsonRequest {
String filename
Integer startLine
Integer maxLines
Expand Down Expand Up @@ -86,19 +87,19 @@ class LogViewerAdminController extends BaseController {
return
}

render(
file: ret.value,
fileName: filename,
contentType: 'application/octet-stream'
)
render(ret)
}

static class Download extends ClusterRequest<File> {
static class Download extends ClusterRequest<Map> {
String filename

File doCall() {
Map doCall() {
if (!availableFiles[filename]) throwUnavailable(filename)
return appContext.logReaderService.get(filename)
def file = appContext.logReaderService.get(filename)
[
file: file.bytes,
fileName: filename,
contentType: 'application/octet-stream'
]
}
}

Expand All @@ -112,7 +113,7 @@ class LogViewerAdminController extends BaseController {
runOnInstance(new DeleteFiles(filenames: params.list('filenames')), instance)
}

static class DeleteFiles extends ClusterRequest {
static class DeleteFiles extends ClusterJsonRequest {
List<String> filenames

def doCall() {
Expand All @@ -138,7 +139,7 @@ class LogViewerAdminController extends BaseController {
runOnInstance(new ArchiveLogs(daysThreshold: daysThreshold), instance)
}

static class ArchiveLogs extends ClusterRequest {
static class ArchiveLogs extends ClusterJsonRequest {
Integer daysThreshold

def doCall() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access

import static io.xh.hoist.util.Utils.appContext
Expand All @@ -21,7 +21,7 @@ class MemoryMonitorAdminController extends BaseController {
def snapshots(String instance) {
runOnInstance(new Snapshots(), instance)
}
static class Snapshots extends ClusterRequest {
static class Snapshots extends ClusterJsonRequest {
def doCall() {
appContext.memoryMonitoringService.snapshots
}
Expand All @@ -31,7 +31,7 @@ class MemoryMonitorAdminController extends BaseController {
def takeSnapshot(String instance) {
runOnInstance(new TakeSnapshot(), instance)
}
static class TakeSnapshot extends ClusterRequest {
static class TakeSnapshot extends ClusterJsonRequest {
def doCall() {
appContext.memoryMonitoringService.takeSnapshot()
}
Expand All @@ -42,7 +42,7 @@ class MemoryMonitorAdminController extends BaseController {
def requestGc(String instance) {
runOnInstance(new RequestGc(), instance)
}
static class RequestGc extends ClusterRequest {
static class RequestGc extends ClusterJsonRequest {
def doCall() {
appContext.memoryMonitoringService.requestGc()
}
Expand All @@ -52,7 +52,7 @@ class MemoryMonitorAdminController extends BaseController {
def dumpHeap(String filename, String instance) {
runOnInstance(new DumpHeap(filename: filename), instance)
}
static class DumpHeap extends ClusterRequest {
static class DumpHeap extends ClusterJsonRequest {
String filename

def doCall() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access

import static io.xh.hoist.util.Utils.appContext
Expand All @@ -18,7 +18,7 @@ class ServiceManagerAdminController extends BaseController {
def listServices(String instance) {
runOnInstance(new ListServices(), instance)
}
static class ListServices extends ClusterRequest {
static class ListServices extends ClusterJsonRequest {
def doCall() {
appContext.serviceManagerService.listServices()
}
Expand All @@ -28,7 +28,7 @@ class ServiceManagerAdminController extends BaseController {
def task = new GetStats(name: name)
runOnInstance(task, instance)
}
static class GetStats extends ClusterRequest {
static class GetStats extends ClusterJsonRequest {
String name
def doCall() {
appContext.serviceManagerService.getStats(name)
Expand All @@ -40,7 +40,7 @@ class ServiceManagerAdminController extends BaseController {
def task = new ClearCaches(names: params.list('names'))
instance ? runOnInstance(task, instance) : runOnAllInstances(task)
}
static class ClearCaches extends ClusterRequest {
static class ClearCaches extends ClusterJsonRequest {
List<String> names

def doCall() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
package io.xh.hoist.admin.cluster

import io.xh.hoist.BaseController
import io.xh.hoist.cluster.ClusterRequest
import io.xh.hoist.cluster.ClusterJsonRequest
import io.xh.hoist.security.Access

import static io.xh.hoist.util.Utils.getAppContext
Expand All @@ -19,17 +19,17 @@ class WebSocketAdminController extends BaseController {
def allChannels(String instance) {
runOnInstance(new AllChannels(), instance)
}
static class AllChannels extends ClusterRequest {
static class AllChannels extends ClusterJsonRequest {
def doCall() {
appContext.webSocketService.allChannels*.formatForJSON()
appContext.webSocketService.allChannels
}
}

@Access(['HOIST_ADMIN'])
def pushToChannel(String channelKey, String topic, String message, String instance) {
runOnInstance(new PushToChannel(channelKey: channelKey, topic: topic, message: message), instance)
}
static class PushToChannel extends ClusterRequest {
static class PushToChannel extends ClusterJsonRequest {
String channelKey
String topic
String message
Expand Down
8 changes: 5 additions & 3 deletions grails-app/services/io/xh/hoist/cluster/ClusterService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import io.xh.hoist.util.Utils
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.ApplicationListener

import java.util.concurrent.Callable

class ClusterService extends BaseService implements ApplicationListener<ApplicationReadyEvent> {

/**
Expand Down Expand Up @@ -143,11 +145,11 @@ class ClusterService extends BaseService implements ApplicationListener<Applicat
return hzInstance.getExecutorService('default')
}

<T> ClusterResponse<T> submitToInstance(ClusterRequest<T> c, String instance) {
executorService.submitToMember(c, getMember(instance)).get()
<T> T submitToInstance(Callable<T> c, String instance) {
executorService.submitToMember(c, getMember(instance)).get()
}

<T> Map<String, ClusterResponse<T>> submitToAllInstances(ClusterRequest<T> c) {
<T> Map<String, T> submitToAllInstances(Callable<T> c) {
executorService
.submitToAllMembers(c)
.collectEntries { member, f -> [member.getAttribute('instanceName'), f.get()] }
Expand Down
Loading

0 comments on commit a4a64f9

Please sign in to comment.