add caching
This commit is contained in:
@@ -1,13 +1,98 @@
|
||||
package model
|
||||
|
||||
type ServiceStatus string
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ServiceStatus int
|
||||
|
||||
const (
|
||||
StatusRunning ServiceStatus = "SERVICE_RUNNING\r\n"
|
||||
StatusStopped ServiceStatus = "SERVICE_STOPPED\r\n"
|
||||
StatusRestarting ServiceStatus = "SERVICE_RESTARTING\r\n"
|
||||
StatusUnknown ServiceStatus = iota
|
||||
StatusStopped
|
||||
StatusStopping
|
||||
StatusRestarting
|
||||
StatusStarting
|
||||
StatusRunning
|
||||
)
|
||||
|
||||
// String converts the ServiceStatus to its string representation
|
||||
func (s ServiceStatus) String() string {
|
||||
switch s {
|
||||
case StatusRunning:
|
||||
return "SERVICE_RUNNING"
|
||||
case StatusStopped:
|
||||
return "SERVICE_STOPPED"
|
||||
case StatusStarting:
|
||||
return "SERVICE_STARTING"
|
||||
case StatusStopping:
|
||||
return "SERVICE_STOPPING"
|
||||
case StatusRestarting:
|
||||
return "SERVICE_RESTARTING"
|
||||
default:
|
||||
return "SERVICE_UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
// ParseServiceStatus converts a string to ServiceStatus
|
||||
func ParseServiceStatus(s string) ServiceStatus {
|
||||
switch s {
|
||||
case "SERVICE_RUNNING":
|
||||
return StatusRunning
|
||||
case "SERVICE_STOPPED":
|
||||
return StatusStopped
|
||||
case "SERVICE_STARTING":
|
||||
return StatusStarting
|
||||
case "SERVICE_STOPPING":
|
||||
return StatusStopping
|
||||
case "SERVICE_RESTARTING":
|
||||
return StatusRestarting
|
||||
default:
|
||||
return StatusUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface
|
||||
func (s ServiceStatus) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + s.String() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface
|
||||
func (s *ServiceStatus) UnmarshalJSON(data []byte) error {
|
||||
str := string(data)
|
||||
// Remove quotes
|
||||
str = str[1 : len(str)-1]
|
||||
*s = ParseServiceStatus(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface
|
||||
func (s *ServiceStatus) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*s = StatusUnknown
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
*s = ParseServiceStatus(v)
|
||||
return nil
|
||||
case []byte:
|
||||
*s = ParseServiceStatus(string(v))
|
||||
return nil
|
||||
case int64:
|
||||
*s = ServiceStatus(v)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported type for ServiceStatus: %T", value)
|
||||
}
|
||||
}
|
||||
|
||||
// Value implements the driver.Valuer interface
|
||||
func (s ServiceStatus) Value() (driver.Value, error) {
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
type ApiModel struct {
|
||||
Api string `json:"api"`
|
||||
}
|
||||
|
||||
120
local/model/cache.go
Normal file
120
local/model/cache.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StatusCache represents a cached server status with expiration
|
||||
type StatusCache struct {
|
||||
Status ServiceStatus
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// CacheConfig holds configuration for cache behavior
|
||||
type CacheConfig struct {
|
||||
ExpirationTime time.Duration // How long before a cache entry expires
|
||||
ThrottleTime time.Duration // Minimum time between status checks
|
||||
DefaultStatus ServiceStatus // Default status to return when throttled
|
||||
}
|
||||
|
||||
// ServerStatusCache manages cached server statuses
|
||||
type ServerStatusCache struct {
|
||||
sync.RWMutex
|
||||
cache map[string]*StatusCache
|
||||
config CacheConfig
|
||||
lastChecked map[string]time.Time
|
||||
}
|
||||
|
||||
// NewServerStatusCache creates a new server status cache
|
||||
func NewServerStatusCache(config CacheConfig) *ServerStatusCache {
|
||||
return &ServerStatusCache{
|
||||
cache: make(map[string]*StatusCache),
|
||||
lastChecked: make(map[string]time.Time),
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// GetStatus retrieves the cached status or indicates if a fresh check is needed
|
||||
func (c *ServerStatusCache) GetStatus(serviceName string) (ServiceStatus, bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
// Check if we're being throttled
|
||||
if lastCheck, exists := c.lastChecked[serviceName]; exists {
|
||||
if time.Since(lastCheck) < c.config.ThrottleTime {
|
||||
if cached, ok := c.cache[serviceName]; ok {
|
||||
return cached.Status, false
|
||||
}
|
||||
return c.config.DefaultStatus, false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have a valid cached entry
|
||||
if cached, ok := c.cache[serviceName]; ok {
|
||||
if time.Since(cached.UpdatedAt) < c.config.ExpirationTime {
|
||||
return cached.Status, false
|
||||
}
|
||||
}
|
||||
|
||||
return StatusUnknown, true
|
||||
}
|
||||
|
||||
// UpdateStatus updates the cache with a new status
|
||||
func (c *ServerStatusCache) UpdateStatus(serviceName string, status ServiceStatus) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.cache[serviceName] = &StatusCache{
|
||||
Status: status,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
c.lastChecked[serviceName] = time.Now()
|
||||
}
|
||||
|
||||
// Clear removes all entries from the cache
|
||||
func (c *ServerStatusCache) Clear() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.cache = make(map[string]*StatusCache)
|
||||
c.lastChecked = make(map[string]time.Time)
|
||||
}
|
||||
|
||||
// LookupCache provides a generic cache for lookup data
|
||||
type LookupCache struct {
|
||||
sync.RWMutex
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
// NewLookupCache creates a new lookup cache
|
||||
func NewLookupCache() *LookupCache {
|
||||
return &LookupCache{
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a cached value by key
|
||||
func (c *LookupCache) Get(key string) (interface{}, bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
value, exists := c.data[key]
|
||||
return value, exists
|
||||
}
|
||||
|
||||
// Set stores a value in the cache
|
||||
func (c *LookupCache) Set(key string, value interface{}) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.data[key] = value
|
||||
}
|
||||
|
||||
// Clear removes all entries from the cache
|
||||
func (c *LookupCache) Clear() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.data = make(map[string]interface{})
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
type Server struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Status ServiceStatus `json:"status"`
|
||||
Status ServiceStatus `json:"status" gorm:"-"`
|
||||
IP string `gorm:"not null" json:"-"`
|
||||
Port int `gorm:"not null" json:"-"`
|
||||
ConfigPath string `gorm:"not null" json:"-"` // e.g. "/acc/servers/server1/"
|
||||
|
||||
Reference in New Issue
Block a user