Skip to content

Instantly share code, notes, and snippets.

@zboralski
Last active June 7, 2023 12:23
Show Gist options
  • Save zboralski/fb5299f309748d3c5f21b86ec7318560 to your computer and use it in GitHub Desktop.
Save zboralski/fb5299f309748d3c5f21b86ec7318560 to your computer and use it in GitHub Desktop.
Temporal Worker GetLimiter
package main
import (
"fmt"
"reflect"
"unsafe"
"go.temporal.io/sdk/worker"
"golang.org/x/time/rate"
)
// GetTaskLimiter can retrieve the taskLimiter from a worker.Worker instance.
// This function uses reflection to access the internal taskLimiter field
// of the worker.Worker instance. Please note that this function serves
// as a workaround to demonstrate the importance and potential usage of making
// taskLimiter accessible, and isn't a recommended long-term solution.
//
// https://github.com/temporalio/sdk-go/issues/1130
func GetTaskLimiter(w worker.Worker) (*rate.Limiter, error) {
if w == nil {
return nil, fmt.Errorf("worker is nil")
}
// Extract the value from the interface
value := reflect.ValueOf(w)
if value.Kind() == reflect.Interface && !value.IsNil() {
value = value.Elem()
}
// If the value is a pointer, dereference it
if value.Kind() == reflect.Ptr && !value.IsNil() {
value = value.Elem()
}
// If the value is now a struct, attempt to get the activityWorker field
if value.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected a struct, but got: %s", value.Kind())
}
activityWorkerField := value.FieldByName("activityWorker")
if !(activityWorkerField.IsValid() && activityWorkerField.CanAddr()) {
return nil, fmt.Errorf("field 'activityWorker' is not valid or can't be addressed")
}
// Create a new Value that treats activityWorker as if it were exported
activityWorkerField = reflect.NewAt(activityWorkerField.Type(), unsafe.Pointer(activityWorkerField.UnsafeAddr())).Elem()
// Check if activityWorker is valid and then access baseWorker
if activityWorkerField.Kind() == reflect.Ptr && !activityWorkerField.IsNil() {
activityWorkerField = activityWorkerField.Elem()
}
if activityWorkerField.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected a struct, but got: %s", activityWorkerField.Kind())
}
baseWorkerField := activityWorkerField.FieldByName("worker")
if !(baseWorkerField.IsValid() && baseWorkerField.CanAddr()) {
return nil, fmt.Errorf("field 'worker' in 'activityWorker' is not valid or can't be addressed")
}
// Create a new Value that treats worker (baseWorker) as if it were exported
baseWorkerField = reflect.NewAt(baseWorkerField.Type(), unsafe.Pointer(baseWorkerField.UnsafeAddr())).Elem()
// Check if baseWorker is valid and then access taskLimiter
if baseWorkerField.Kind() == reflect.Ptr && !baseWorkerField.IsNil() {
baseWorkerField = baseWorkerField.Elem()
}
if baseWorkerField.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected a struct, but got: %s", baseWorkerField.Kind())
}
taskLimiterField := baseWorkerField.FieldByName("taskLimiter")
if !(taskLimiterField.IsValid() && taskLimiterField.CanAddr()) {
return nil, fmt.Errorf("field 'taskLimiter' in 'baseWorker' is not valid or can't be addressed")
}
// Finally return the taskLimiter as *rate.Limiter
return (*rate.Limiter)(unsafe.Pointer(taskLimiterField.Pointer())), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment