Files
oci-portal/internal/oci/cached.go
T

286 lines
11 KiB
Go

package oci
import (
"context"
"slices"
"strings"
"time"
"oci-portal/internal/cache"
)
// 读缓存 TTL 分级:过渡态资源短、准静态长(调研①方案 A)。
// 实例列表 10s:前端 5s 自动刷新隔次命中,OCI 调用减半,状态延迟可控。
const (
cacheTTLInstances = 10 * time.Second
cacheTTLVolatile = 30 * time.Second // 卷 / 挂载 / 网卡
cacheTTLNetwork = 2 * time.Minute // VCN / 子网 / 安全列表
cacheTTLStatic = 10 * time.Minute // 镜像 / 可用域
)
// CachedClient 用进程内 TTL 缓存包装真实客户端,削减 OCI 读放大;
// 写操作直通并失效同租户全部读缓存(粗粒度换正确性,重建仅一次回源)。
// 未覆写的方法经嵌入接口直通。
type CachedClient struct {
Client
store *cache.Cache
}
// NewCachedClient 包装 inner;上限 4096 键(单键几 KB,整体几 MB 级)。
func NewCachedClient(inner Client) *CachedClient {
return &CachedClient{Client: inner, store: cache.New(4096)}
}
// ckey 组缓存键;租户 OCID 在最前,写失效按前缀一锅端。
func ckey(cred Credentials, parts ...string) string {
return cred.TenancyOCID + "|" + strings.Join(parts, "|")
}
// bust 写操作成功后失效该租户全部读缓存。
func (c *CachedClient) bust(err error, cred Credentials) {
if err == nil {
c.store.DeletePrefix(cred.TenancyOCID + "|")
}
}
// cachedList 缓存切片结果;命中返回浅拷贝,防调用方排序共享底层数组。
func cachedList[T any](c *CachedClient, key string, ttl time.Duration, fn func() ([]T, error)) ([]T, error) {
v, err := cache.Do(c.store, key, ttl, fn)
if err != nil {
return nil, err
}
return slices.Clone(v), nil
}
// ---- 读:按 TTL 分级缓存 ----
func (c *CachedClient) ListInstances(ctx context.Context, cred Credentials, region string) ([]Instance, error) {
return cachedList(c, ckey(cred, "instances", region), cacheTTLInstances, func() ([]Instance, error) {
return c.Client.ListInstances(ctx, cred, region)
})
}
func (c *CachedClient) ListImages(ctx context.Context, cred Credentials, q ImagesQuery) ([]Image, error) {
return cachedList(c, ckey(cred, "images", q.Region, q.OperatingSystem, q.Shape), cacheTTLStatic, func() ([]Image, error) {
return c.Client.ListImages(ctx, cred, q)
})
}
func (c *CachedClient) ListVCNs(ctx context.Context, cred Credentials, region string) ([]VCN, error) {
return cachedList(c, ckey(cred, "vcns", region), cacheTTLNetwork, func() ([]VCN, error) {
return c.Client.ListVCNs(ctx, cred, region)
})
}
func (c *CachedClient) ListSubnets(ctx context.Context, cred Credentials, region, vcnID string) ([]Subnet, error) {
return cachedList(c, ckey(cred, "subnets", region, vcnID), cacheTTLNetwork, func() ([]Subnet, error) {
return c.Client.ListSubnets(ctx, cred, region, vcnID)
})
}
func (c *CachedClient) ListSecurityLists(ctx context.Context, cred Credentials, region, vcnID string) ([]SecurityList, error) {
return cachedList(c, ckey(cred, "seclists", region, vcnID), cacheTTLNetwork, func() ([]SecurityList, error) {
return c.Client.ListSecurityLists(ctx, cred, region, vcnID)
})
}
func (c *CachedClient) ListAvailabilityDomains(ctx context.Context, cred Credentials, region string) ([]string, error) {
return cachedList(c, ckey(cred, "ads", region), cacheTTLStatic, func() ([]string, error) {
return c.Client.ListAvailabilityDomains(ctx, cred, region)
})
}
func (c *CachedClient) ListBootVolumes(ctx context.Context, cred Credentials, region, availabilityDomain string) ([]BootVolume, error) {
return cachedList(c, ckey(cred, "bootvols", region, availabilityDomain), cacheTTLVolatile, func() ([]BootVolume, error) {
return c.Client.ListBootVolumes(ctx, cred, region, availabilityDomain)
})
}
func (c *CachedClient) ListBlockVolumes(ctx context.Context, cred Credentials, region, availabilityDomain string) ([]BlockVolume, error) {
return cachedList(c, ckey(cred, "blockvols", region, availabilityDomain), cacheTTLVolatile, func() ([]BlockVolume, error) {
return c.Client.ListBlockVolumes(ctx, cred, region, availabilityDomain)
})
}
func (c *CachedClient) ListInstanceVnics(ctx context.Context, cred Credentials, region, instanceID string) ([]Vnic, error) {
return cachedList(c, ckey(cred, "vnics", region, instanceID), cacheTTLVolatile, func() ([]Vnic, error) {
return c.Client.ListInstanceVnics(ctx, cred, region, instanceID)
})
}
func (c *CachedClient) ListBootVolumeAttachments(ctx context.Context, cred Credentials, region, instanceID string) ([]BootVolumeAttachment, error) {
return cachedList(c, ckey(cred, "bvattach", region, instanceID), cacheTTLVolatile, func() ([]BootVolumeAttachment, error) {
return c.Client.ListBootVolumeAttachments(ctx, cred, region, instanceID)
})
}
func (c *CachedClient) ListVolumeAttachments(ctx context.Context, cred Credentials, region, instanceID string) ([]VolumeAttachment, error) {
return cachedList(c, ckey(cred, "volattach", region, instanceID), cacheTTLVolatile, func() ([]VolumeAttachment, error) {
return c.Client.ListVolumeAttachments(ctx, cred, region, instanceID)
})
}
// ---- 写:直通,成功后按租户失效 ----
func (c *CachedClient) LaunchInstance(ctx context.Context, cred Credentials, in CreateInstanceInput) (Instance, error) {
out, err := c.Client.LaunchInstance(ctx, cred, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) UpdateInstance(ctx context.Context, cred Credentials, instanceID string, in UpdateInstanceInput) (Instance, error) {
out, err := c.Client.UpdateInstance(ctx, cred, instanceID, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) TerminateInstance(ctx context.Context, cred Credentials, region, instanceID string, preserveBootVolume bool) error {
err := c.Client.TerminateInstance(ctx, cred, region, instanceID, preserveBootVolume)
c.bust(err, cred)
return err
}
func (c *CachedClient) InstanceAction(ctx context.Context, cred Credentials, region, instanceID, action string) (Instance, error) {
out, err := c.Client.InstanceAction(ctx, cred, region, instanceID, action)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) ChangeInstancePublicIP(ctx context.Context, cred Credentials, region, instanceID string) (string, error) {
out, err := c.Client.ChangeInstancePublicIP(ctx, cred, region, instanceID)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) AddInstanceIpv6(ctx context.Context, cred Credentials, region, instanceID, address string) (string, error) {
out, err := c.Client.AddInstanceIpv6(ctx, cred, region, instanceID, address)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DeleteInstanceIpv6(ctx context.Context, cred Credentials, region, instanceID, address string) error {
err := c.Client.DeleteInstanceIpv6(ctx, cred, region, instanceID, address)
c.bust(err, cred)
return err
}
func (c *CachedClient) CreateVCN(ctx context.Context, cred Credentials, in CreateVCNInput) (VCN, error) {
out, err := c.Client.CreateVCN(ctx, cred, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) UpdateVCN(ctx context.Context, cred Credentials, region, vcnID, displayName string) (VCN, error) {
out, err := c.Client.UpdateVCN(ctx, cred, region, vcnID, displayName)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DeleteVCN(ctx context.Context, cred Credentials, region, vcnID string) error {
err := c.Client.DeleteVCN(ctx, cred, region, vcnID)
c.bust(err, cred)
return err
}
func (c *CachedClient) EnableVCNIPv6(ctx context.Context, cred Credentials, region, vcnID string) ([]IPv6Step, error) {
out, err := c.Client.EnableVCNIPv6(ctx, cred, region, vcnID)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) CreateSubnet(ctx context.Context, cred Credentials, in CreateSubnetInput) (Subnet, error) {
out, err := c.Client.CreateSubnet(ctx, cred, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) UpdateSubnet(ctx context.Context, cred Credentials, region, subnetID, displayName string) (Subnet, error) {
out, err := c.Client.UpdateSubnet(ctx, cred, region, subnetID, displayName)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DeleteSubnet(ctx context.Context, cred Credentials, region, subnetID string) error {
err := c.Client.DeleteSubnet(ctx, cred, region, subnetID)
c.bust(err, cred)
return err
}
func (c *CachedClient) CreateSecurityList(ctx context.Context, cred Credentials, in CreateSecurityListInput) (SecurityList, error) {
out, err := c.Client.CreateSecurityList(ctx, cred, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) UpdateSecurityList(ctx context.Context, cred Credentials, securityListID string, in UpdateSecurityListInput) (SecurityList, error) {
out, err := c.Client.UpdateSecurityList(ctx, cred, securityListID, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DeleteSecurityList(ctx context.Context, cred Credentials, region, securityListID string) error {
err := c.Client.DeleteSecurityList(ctx, cred, region, securityListID)
c.bust(err, cred)
return err
}
func (c *CachedClient) UpdateBootVolume(ctx context.Context, cred Credentials, bootVolumeID string, in UpdateBootVolumeInput) (BootVolume, error) {
out, err := c.Client.UpdateBootVolume(ctx, cred, bootVolumeID, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DeleteBootVolume(ctx context.Context, cred Credentials, region, bootVolumeID string) error {
err := c.Client.DeleteBootVolume(ctx, cred, region, bootVolumeID)
c.bust(err, cred)
return err
}
func (c *CachedClient) AttachBootVolume(ctx context.Context, cred Credentials, region, instanceID, bootVolumeID string) (BootVolumeAttachment, error) {
out, err := c.Client.AttachBootVolume(ctx, cred, region, instanceID, bootVolumeID)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DetachBootVolume(ctx context.Context, cred Credentials, region, attachmentID string) error {
err := c.Client.DetachBootVolume(ctx, cred, region, attachmentID)
c.bust(err, cred)
return err
}
func (c *CachedClient) ReplaceBootVolume(ctx context.Context, cred Credentials, region, instanceID, newBootVolumeID string) (BootVolumeAttachment, error) {
out, err := c.Client.ReplaceBootVolume(ctx, cred, region, instanceID, newBootVolumeID)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) AttachVolume(ctx context.Context, cred Credentials, region, instanceID, volumeID string, readOnly bool) (VolumeAttachment, error) {
out, err := c.Client.AttachVolume(ctx, cred, region, instanceID, volumeID, readOnly)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DetachVolume(ctx context.Context, cred Credentials, region, attachmentID string) error {
err := c.Client.DetachVolume(ctx, cred, region, attachmentID)
c.bust(err, cred)
return err
}
func (c *CachedClient) AttachVnic(ctx context.Context, cred Credentials, region, instanceID string, in AttachVnicInput) (Vnic, error) {
out, err := c.Client.AttachVnic(ctx, cred, region, instanceID, in)
c.bust(err, cred)
return out, err
}
func (c *CachedClient) DetachVnic(ctx context.Context, cred Credentials, region, attachmentID string) error {
err := c.Client.DetachVnic(ctx, cred, region, attachmentID)
c.bust(err, cred)
return err
}
func (c *CachedClient) AddVnicIpv6(ctx context.Context, cred Credentials, region, vnicID, address string) (string, error) {
out, err := c.Client.AddVnicIpv6(ctx, cred, region, vnicID, address)
c.bust(err, cred)
return out, err
}