func compareChanged(changed, changable []string) bool {
	if len(changed) > len(changable) {
		return false
	}
	diff := make(map[string]bool, len(changed))
	for _, elem := range changed {
		diff[elem] = true
	}
	for _, elem := range changable {
		delete(diff, elem)
	}
	return len(diff) == 0
}
this returns true if only changable fields have been changed
package handlers
import (
	"reflect"
	"strconv"
	client_native "github.com/haproxytech/client-native/v2"
	"github.com/haproxytech/client-native/v2/models"
	"github.com/haproxytech/dataplaneapi/log"
)
var RuntimeSupportedFields = map[string][]string{
	"frontend": {"Maxconn"},
	"server":   {"Weight", "Address", "Port", "Maintenance", "AgentCheck", "AgentAddr", "AgentSend", "HealthCheckPort"},
}
func changeThroughRuntimeAPI(data, ondisk interface{}, parentName, parentType string, client *client_native.HAProxyClient) (reload bool) {
	defer func() {
		if r := recover(); r != nil {
			log.Warning("Change Through API Panic: ", r)
			reload = true
		}
	}()
	if client.Runtime == nil {
		return true
	}
	diff := compareObjects(data, ondisk)
	if len(diff) == 0 {
		return false
	}
	switch vData := data.(type) {
	case models.Frontend:
		if compareChanged(diff, RuntimeSupportedFields["frontend"]) {
			for _, field := range diff {
				fieldValue := reflect.ValueOf(vData).FieldByName(field)
				if fieldValue.IsValid() {
					switch field {
					case "Maxconn":
						maxConn := fieldValue.Elem().Int()
						err := client.Runtime.SetFrontendMaxConn(vData.Name, int(maxConn))
						if err != nil {
							return true
						}
					}
				}
			}
		} else {
			return true
		}
	case models.Server:
		if compareChanged(diff, RuntimeSupportedFields["server"]) {
			addrPortChanged := false
			for _, field := range diff {
				fieldValue := reflect.ValueOf(vData).FieldByName(field)
				if fieldValue.IsValid() {
					switch field {
					case "Weight":
						weight := strconv.FormatInt(fieldValue.Elem().Int(), 10)
						err := client.Runtime.SetServerWeight(parentName, vData.Name, weight)
						if err != nil {
							return true
						}
					case "HealthCheckPort":
						portVal := fieldValue.Elem().Int()
						err := client.Runtime.SetServerCheckPort(parentName, vData.Name, int(portVal))
						if err != nil {
							return true
						}
					case "Address":
						if !addrPortChanged {
							portVal := reflect.ValueOf(vData).FieldByName("Port").Elem().Int()
							addrVal := fieldValue.String()
							err := client.Runtime.SetServerAddr(parentName, vData.Name, addrVal, int(portVal))
							if err != nil {
								return true
							}
							addrPortChanged = true
						}
					case "Port":
						if !addrPortChanged {
							portVal := fieldValue.Elem().Int()
							addrVal := reflect.ValueOf(vData).FieldByName("Address").String()
							err := client.Runtime.SetServerAddr(parentName, vData.Name, addrVal, int(portVal))
							if err != nil {
								return true
							}
							addrPortChanged = true
						}
					case "Maintenance":
						maint := fieldValue.String()
						if maint == "enabled" {
							maint = "maint"
						} else {
							maint = "ready"
						}
						err := client.Runtime.SetServerState(parentName, vData.Name, maint)
						if err != nil {
							return true
						}
					case "AgentCheck":
						aCheck := fieldValue.String()
						if aCheck == "enabled" {
							err := client.Runtime.EnableAgentCheck(parentName, vData.Name)
							if err != nil {
								return true
							}
						}
						if aCheck == "disabled" {
							err := client.Runtime.DisableAgentCheck(parentName, vData.Name)
							if err != nil {
								return true
							}
						}
					case "AgentAddr":
						aAddr := fieldValue.String()
						err := client.Runtime.SetServerAgentAddr(parentName, vData.Name, aAddr)
						if err != nil {
							return true
						}
					case "AgentSend":
						aSend := fieldValue.String()
						err := client.Runtime.SetServerAgentSend(parentName, vData.Name, aSend)
						if err != nil {
							return true
						}
					}
				}
			}
		} else {
			return true
		}
	default:
		return true
	}
	return false
}
func compareObjects(data, ondisk interface{}) []string {
	diff := make([]string, 0)
	dataVal := reflect.ValueOf(data)
	ondiskVal := reflect.ValueOf(ondisk)
	for i := 0; i < dataVal.NumField(); i++ {
		fName := dataVal.Type().Field(i).Name
		dataField := dataVal.FieldByName(fName)
		ondiskField := ondiskVal.FieldByName(fName)
		dataKind := dataField.Kind()
		ondiskKind := ondiskField.Kind()
		if dataKind != ondiskKind {
			diff = append(diff, fName)
			continue
		}
		if dataKind == reflect.Ptr {
			dataField = dataField.Elem()
			ondiskField = ondiskField.Elem()
			dataKind = dataField.Kind()
			ondiskKind = ondiskField.Kind()
			if dataKind != ondiskKind {
				diff = append(diff, fName)
				continue
			}
		}
		switch dataKind {
		case reflect.Float32, reflect.Float64:
			if dataField.Float() != ondiskField.Float() {
				diff = append(diff, fName)
			}
		case reflect.Bool:
			if dataField.Bool() != ondiskField.Bool() {
				diff = append(diff, fName)
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if dataField.Int() != ondiskField.Int() {
				diff = append(diff, fName)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			if dataField.Uint() != ondiskField.Uint() {
				diff = append(diff, fName)
			}
		case reflect.String:
			if dataField.String() != ondiskField.String() {
				diff = append(diff, fName)
			}
		}
	}
	return diff
}