303 lines
12 KiB
Go
303 lines
12 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 在最前,写失效按前缀一锅端。
|
|
// compartment 必须参与键值:列表查询按 EffectiveCompartment 过滤,
|
|
// 同租户切换区间时若共用键会串到上一个区间的缓存结果。
|
|
func ckey(cred Credentials, parts ...string) string {
|
|
return cred.TenancyOCID + "|" + cred.CompartmentID + "|" + strings.Join(parts, "|")
|
|
}
|
|
|
|
// bust 写操作成功后失效该租户全部读缓存。
|
|
func (c *CachedClient) bust(err error, cred Credentials) {
|
|
if err == nil {
|
|
c.InvalidateTenancy(cred.TenancyOCID)
|
|
}
|
|
}
|
|
|
|
// InvalidateTenancy 主动失效指定 tenancy 的全部 OCI 读缓存。
|
|
func (c *CachedClient) InvalidateTenancy(tenancyOCID string) {
|
|
if tenancyOCID == "" {
|
|
return
|
|
}
|
|
c.store.DeletePrefix(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)
|
|
})
|
|
}
|
|
|
|
// ListIdentityDomains 域列表准静态且是所有按域操作的 URL 解析源,长 TTL 缓存。
|
|
func (c *CachedClient) ListIdentityDomains(ctx context.Context, cred Credentials, region string) ([]IdentityDomain, error) {
|
|
return cachedList(c, ckey(cred, "iddomains", region), cacheTTLStatic, func() ([]IdentityDomain, error) {
|
|
return c.Client.ListIdentityDomains(ctx, cred, region)
|
|
})
|
|
}
|
|
|
|
// ---- 写:直通,成功后按租户失效 ----
|
|
|
|
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
|
|
}
|