Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support remote Json Serialization in runOnInstance #424

Merged
merged 7 commits into from
Nov 29, 2024
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
Loading