This is an automated email from the ASF dual-hosted git repository.

bzp2010 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git


The following commit(s) were added to refs/heads/master by this push:
     new 3f7475e4 feat(cache): rewrite global_rules and plugin_metadata as 
owned rows (#2884)
3f7475e4 is described below

commit 3f7475e4bdb9a8a777fa5d4d7b95d029250b9468
Author: Zeping Bai <[email protected]>
AuthorDate: Fri Sep 18 18:27:00 2026 +0800

    feat(cache): rewrite global_rules and plugin_metadata as owned rows (#2884)
---
 api/adc/types.go                        |   7 -
 api/adc/zz_generated.deepcopy.go        |  17 --
 internal/adc/cache/cache.go             |  61 +++++-
 internal/adc/cache/indexer.go           |  24 +++
 internal/adc/cache/memdb.go             |  54 +++++-
 internal/adc/cache/noop_db.go           |  24 ++-
 internal/adc/cache/schema.go            |  19 +-
 internal/adc/cache/store.go             | 332 +++++++++++++++++---------------
 internal/adc/cache/store_test.go        |  85 +++++++-
 internal/provider/apisix/status.go      |  17 +-
 internal/provider/apisix/status_test.go |  38 ++--
 11 files changed, 438 insertions(+), 240 deletions(-)

diff --git a/api/adc/types.go b/api/adc/types.go
index cbda8c66..475ff1a6 100644
--- a/api/adc/types.go
+++ b/api/adc/types.go
@@ -92,13 +92,6 @@ func (g *GlobalRule) DeepCopy() GlobalRule {
        return GlobalRule(copied)
 }
 
-// +k8s:deepcopy-gen=true
-type GlobalRuleItem struct {
-       Metadata `json:",inline" yaml:",inline"`
-
-       Plugins Plugins `json:"plugins" yaml:"plugins"`
-}
-
 type PluginMetadata Plugins
 
 func (p *PluginMetadata) DeepCopy() PluginMetadata {
diff --git a/api/adc/zz_generated.deepcopy.go b/api/adc/zz_generated.deepcopy.go
index 90ce2f81..b117ad34 100644
--- a/api/adc/zz_generated.deepcopy.go
+++ b/api/adc/zz_generated.deepcopy.go
@@ -246,23 +246,6 @@ func (in *ForwardAuthConfig) DeepCopy() *ForwardAuthConfig 
{
        return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
-func (in *GlobalRuleItem) DeepCopyInto(out *GlobalRuleItem) {
-       *out = *in
-       in.Metadata.DeepCopyInto(&out.Metadata)
-       out.Plugins = in.Plugins.DeepCopy()
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new GlobalRuleItem.
-func (in *GlobalRuleItem) DeepCopy() *GlobalRuleItem {
-       if in == nil {
-               return nil
-       }
-       out := new(GlobalRuleItem)
-       in.DeepCopyInto(out)
-       return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *HMACAuthConsumerConfig) DeepCopyInto(out *HMACAuthConsumerConfig) {
        *out = *in
diff --git a/internal/adc/cache/cache.go b/internal/adc/cache/cache.go
index db3f6cb9..198e301b 100644
--- a/internal/adc/cache/cache.go
+++ b/internal/adc/cache/cache.go
@@ -19,8 +19,40 @@ package cache
 
 import (
        types "github.com/apache/apisix-ingress-controller/api/adc"
+       internaltypes 
"github.com/apache/apisix-ingress-controller/internal/types"
 )
 
+// GlobalRuleRow is one global_rules plugin, keyed by the plugin name. Owner 
is the
+// Kubernetes resource that declared it: a GatewayProxy or an ApisixGlobalRule.
+type GlobalRuleRow struct {
+       ID     string
+       Owner  internaltypes.NamespacedNameKind
+       Config any
+}
+
+func (r *GlobalRuleRow) DeepCopy() *GlobalRuleRow {
+       out := *r
+       out.Config = copyPluginConfig(r.ID, r.Config)
+       return &out
+}
+
+// PluginMetadataRow is one plugin_metadata entry, keyed by the plugin name. 
It needs no
+// owner: its only source is the GatewayProxy the cacheKey itself names.
+type PluginMetadataRow struct {
+       ID     string
+       Config any
+}
+
+func (r *PluginMetadataRow) DeepCopy() *PluginMetadataRow {
+       out := *r
+       out.Config = copyPluginConfig(r.ID, r.Config)
+       return &out
+}
+
+func copyPluginConfig(name string, config any) any {
+       return types.Plugins{name: config}.DeepCopy()[name]
+}
+
 type Cache interface {
        Insert(obj any) error
        Delete(obj any) error
@@ -32,7 +64,9 @@ type Cache interface {
        // InsertConsumer adds or updates consumer to cache.
        InsertConsumer(*types.Consumer) error
        // InsertGlobalRule adds or updates global rule to cache.
-       InsertGlobalRule(*types.GlobalRuleItem) error
+       InsertGlobalRule(*GlobalRuleRow) error
+       // InsertPluginMetadata adds or updates plugin metadata to cache.
+       InsertPluginMetadata(*PluginMetadataRow) error
 
        // GetSSL finds the ssl from cache according to the primary index (id).
        GetSSL(string) (*types.SSL, error)
@@ -41,7 +75,9 @@ type Cache interface {
        // GetConsumer finds the consumer from cache according to the primary 
index (username).
        GetConsumer(string) (*types.Consumer, error)
        // GetGlobalRule finds the global rule from cache according to the 
primary index (id).
-       GetGlobalRule(string) (*types.GlobalRuleItem, error)
+       GetGlobalRule(string) (*GlobalRuleRow, error)
+       // GetPluginMetadata finds the plugin metadata from cache according to 
the primary index (id).
+       GetPluginMetadata(string) (*PluginMetadataRow, error)
 
        // DeleteSSL deletes the specified ssl in cache.
        DeleteSSL(*types.SSL) error
@@ -50,7 +86,9 @@ type Cache interface {
        // DeleteConsumer deletes the specified consumer in cache.
        DeleteConsumer(*types.Consumer) error
        // DeleteGlobalRule deletes the specified global rule in cache.
-       DeleteGlobalRule(*types.GlobalRuleItem) error
+       DeleteGlobalRule(*GlobalRuleRow) error
+       // DeletePluginMetadata deletes the specified plugin metadata in cache.
+       DeletePluginMetadata(*PluginMetadataRow) error
 
        // ListSSL lists all ssl objects in cache.
        ListSSL(...ListOption) ([]*types.SSL, error)
@@ -59,7 +97,9 @@ type Cache interface {
        // ListConsumers lists all consumer objects in cache.
        ListConsumers(...ListOption) ([]*types.Consumer, error)
        // ListGlobalRules lists all global rule objects in cache.
-       ListGlobalRules(...ListOption) ([]*types.GlobalRuleItem, error)
+       ListGlobalRules(...ListOption) ([]*GlobalRuleRow, error)
+       // ListPluginMetadata lists all plugin metadata objects in cache.
+       ListPluginMetadata(...ListOption) ([]*PluginMetadataRow, error)
 }
 
 type ListOption interface {
@@ -68,12 +108,16 @@ type ListOption interface {
 
 type ListOptions struct {
        KindLabelSelector *KindLabelSelector
+       OwnerSelector     *OwnerSelector
 }
 
 func (o *ListOptions) ApplyToList(lo *ListOptions) {
        if o.KindLabelSelector != nil {
                lo.KindLabelSelector = o.KindLabelSelector
        }
+       if o.OwnerSelector != nil {
+               lo.OwnerSelector = o.OwnerSelector
+       }
 }
 
 func (o *ListOptions) ApplyOptions(opts []ListOption) *ListOptions {
@@ -92,3 +136,12 @@ type KindLabelSelector struct {
 func (o *KindLabelSelector) ApplyToList(opts *ListOptions) {
        opts.KindLabelSelector = o
 }
+
+// OwnerSelector lists only the global rules declared by Owner.
+type OwnerSelector struct {
+       Owner internaltypes.NamespacedNameKind
+}
+
+func (o *OwnerSelector) ApplyToList(opts *ListOptions) {
+       opts.OwnerSelector = o
+}
diff --git a/internal/adc/cache/indexer.go b/internal/adc/cache/indexer.go
index ad9dda3b..d9dc62c5 100644
--- a/internal/adc/cache/indexer.go
+++ b/internal/adc/cache/indexer.go
@@ -23,10 +23,12 @@ import (
 
        "github.com/apache/apisix-ingress-controller/api/adc"
        "github.com/apache/apisix-ingress-controller/internal/controller/label"
+       "github.com/apache/apisix-ingress-controller/internal/types"
 )
 
 const (
        KindLabelIndex = "label"
+       OwnerIndex     = "owner"
 )
 
 /*
@@ -94,3 +96,25 @@ func (emi *LabelIndexer) FromArgs(args ...any) ([]byte, 
error) {
 
        return emi.genKey(labelValues), nil
 }
+
+// ownerIndexer indexes a GlobalRuleRow by its Owner.
+type ownerIndexer struct{}
+
+func (ownerIndexer) FromObject(obj any) (bool, []byte, error) {
+       row, ok := obj.(*GlobalRuleRow)
+       if !ok {
+               return false, nil, fmt.Errorf("unexpected object type %T", obj)
+       }
+       return true, []byte(row.Owner.String() + "\x00"), nil
+}
+
+func (ownerIndexer) FromArgs(args ...any) ([]byte, error) {
+       if len(args) != 1 {
+               return nil, fmt.Errorf("expected 1 argument, got %d", len(args))
+       }
+       owner, ok := args[0].(types.NamespacedNameKind)
+       if !ok {
+               return nil, fmt.Errorf("argument is not a NamespacedNameKind")
+       }
+       return []byte(owner.String() + "\x00"), nil
+}
diff --git a/internal/adc/cache/memdb.go b/internal/adc/cache/memdb.go
index 50f2a9f2..83db0b67 100644
--- a/internal/adc/cache/memdb.go
+++ b/internal/adc/cache/memdb.go
@@ -55,8 +55,10 @@ func (c *dbCache) Insert(obj any) error {
                return c.InsertService(t)
        case *types.Consumer:
                return c.InsertConsumer(t)
-       case *types.GlobalRuleItem:
+       case *GlobalRuleRow:
                return c.InsertGlobalRule(t)
+       case *PluginMetadataRow:
+               return c.InsertPluginMetadata(t)
        default:
                return errors.New("unsupported type")
        }
@@ -72,8 +74,10 @@ func (c *dbCache) Delete(obj any) error {
                return c.DeleteService(t)
        case *types.Consumer:
                return c.DeleteConsumer(t)
-       case *types.GlobalRuleItem:
+       case *GlobalRuleRow:
                return c.DeleteGlobalRule(t)
+       case *PluginMetadataRow:
+               return c.DeletePluginMetadata(t)
        default:
                return errors.New("unsupported type")
        }
@@ -96,10 +100,14 @@ func (c *dbCache) InsertConsumer(consumer *types.Consumer) 
error {
        return c.insert(types.TypeConsumer, consumer.DeepCopy())
 }
 
-func (c *dbCache) InsertGlobalRule(globalRule *types.GlobalRuleItem) error {
+func (c *dbCache) InsertGlobalRule(globalRule *GlobalRuleRow) error {
        return c.insert(types.TypeGlobalRule, globalRule.DeepCopy())
 }
 
+func (c *dbCache) InsertPluginMetadata(pluginMetadata *PluginMetadataRow) 
error {
+       return c.insert(types.TypePluginMetadata, pluginMetadata.DeepCopy())
+}
+
 func (c *dbCache) insert(table string, obj any) error {
        txn := c.db.Txn(true)
        defer txn.Abort()
@@ -142,12 +150,20 @@ func (c *dbCache) GetConsumer(username string) 
(*types.Consumer, error) {
        return obj.(*types.Consumer).DeepCopy(), nil
 }
 
-func (c *dbCache) GetGlobalRule(id string) (*types.GlobalRuleItem, error) {
+func (c *dbCache) GetGlobalRule(id string) (*GlobalRuleRow, error) {
        obj, err := c.get(types.TypeGlobalRule, id)
        if err != nil {
                return nil, err
        }
-       return obj.(*types.GlobalRuleItem).DeepCopy(), nil
+       return obj.(*GlobalRuleRow).DeepCopy(), nil
+}
+
+func (c *dbCache) GetPluginMetadata(id string) (*PluginMetadataRow, error) {
+       obj, err := c.get(types.TypePluginMetadata, id)
+       if err != nil {
+               return nil, err
+       }
+       return obj.(*PluginMetadataRow).DeepCopy(), nil
 }
 
 func (c *dbCache) GetStreamRoute(id string) (*types.StreamRoute, error) {
@@ -222,18 +238,30 @@ func (c *dbCache) ListConsumers(opts ...ListOption) 
([]*types.Consumer, error) {
        return consumers, nil
 }
 
-func (c *dbCache) ListGlobalRules(opts ...ListOption) 
([]*types.GlobalRuleItem, error) {
+func (c *dbCache) ListGlobalRules(opts ...ListOption) ([]*GlobalRuleRow, 
error) {
        raws, err := c.list(types.TypeGlobalRule, opts...)
        if err != nil {
                return nil, err
        }
-       globalRules := make([]*types.GlobalRuleItem, 0, len(raws))
+       globalRules := make([]*GlobalRuleRow, 0, len(raws))
        for _, raw := range raws {
-               globalRules = append(globalRules, 
raw.(*types.GlobalRuleItem).DeepCopy())
+               globalRules = append(globalRules, 
raw.(*GlobalRuleRow).DeepCopy())
        }
        return globalRules, nil
 }
 
+func (c *dbCache) ListPluginMetadata(opts ...ListOption) 
([]*PluginMetadataRow, error) {
+       raws, err := c.list(types.TypePluginMetadata, opts...)
+       if err != nil {
+               return nil, err
+       }
+       pluginMetadata := make([]*PluginMetadataRow, 0, len(raws))
+       for _, raw := range raws {
+               pluginMetadata = append(pluginMetadata, 
raw.(*PluginMetadataRow).DeepCopy())
+       }
+       return pluginMetadata, nil
+}
+
 func (c *dbCache) list(table string, opts ...ListOption) ([]any, error) {
        txn := c.db.Txn(false)
        defer txn.Abort()
@@ -245,6 +273,10 @@ func (c *dbCache) list(table string, opts ...ListOption) 
([]any, error) {
                index = KindLabelIndex
                args = []any{listOpts.KindLabelSelector.Kind, 
listOpts.KindLabelSelector.Namespace, listOpts.KindLabelSelector.Name}
        }
+       if listOpts.OwnerSelector != nil {
+               index = OwnerIndex
+               args = []any{listOpts.OwnerSelector.Owner}
+       }
        iter, err := txn.Get(table, index, args...)
        if err != nil {
                return nil, err
@@ -272,10 +304,14 @@ func (c *dbCache) DeleteConsumer(consumer 
*types.Consumer) error {
        return c.delete(types.TypeConsumer, consumer)
 }
 
-func (c *dbCache) DeleteGlobalRule(globalRule *types.GlobalRuleItem) error {
+func (c *dbCache) DeleteGlobalRule(globalRule *GlobalRuleRow) error {
        return c.delete(types.TypeGlobalRule, globalRule)
 }
 
+func (c *dbCache) DeletePluginMetadata(pluginMetadata *PluginMetadataRow) 
error {
+       return c.delete(types.TypePluginMetadata, pluginMetadata)
+}
+
 func (c *dbCache) delete(table string, obj any) error {
        txn := c.db.Txn(true)
        defer txn.Abort()
diff --git a/internal/adc/cache/noop_db.go b/internal/adc/cache/noop_db.go
index f18ef1ca..878c7210 100644
--- a/internal/adc/cache/noop_db.go
+++ b/internal/adc/cache/noop_db.go
@@ -45,7 +45,11 @@ func (c *noopCache) InsertService(u *types.Service) error {
        return nil
 }
 
-func (c *noopCache) InsertGlobalRule(gr *types.GlobalRuleItem) error {
+func (c *noopCache) InsertGlobalRule(gr *GlobalRuleRow) error {
+       return nil
+}
+
+func (c *noopCache) InsertPluginMetadata(pm *PluginMetadataRow) error {
        return nil
 }
 
@@ -61,7 +65,11 @@ func (c *noopCache) GetService(id string) (*types.Service, 
error) {
        return nil, nil
 }
 
-func (c *noopCache) GetGlobalRule(id string) (*types.GlobalRuleItem, error) {
+func (c *noopCache) GetGlobalRule(id string) (*GlobalRuleRow, error) {
+       return nil, nil
+}
+
+func (c *noopCache) GetPluginMetadata(id string) (*PluginMetadataRow, error) {
        return nil, nil
 }
 
@@ -81,7 +89,11 @@ func (c *noopCache) ListStreamRoutes(...ListOption) 
([]*types.StreamRoute, error
        return nil, nil
 }
 
-func (c *noopCache) ListGlobalRules(...ListOption) ([]*types.GlobalRuleItem, 
error) {
+func (c *noopCache) ListGlobalRules(...ListOption) ([]*GlobalRuleRow, error) {
+       return nil, nil
+}
+
+func (c *noopCache) ListPluginMetadata(...ListOption) ([]*PluginMetadataRow, 
error) {
        return nil, nil
 }
 
@@ -97,7 +109,11 @@ func (c *noopCache) DeleteService(u *types.Service) error {
        return nil
 }
 
-func (c *noopCache) DeleteGlobalRule(gr *types.GlobalRuleItem) error {
+func (c *noopCache) DeleteGlobalRule(gr *GlobalRuleRow) error {
+       return nil
+}
+
+func (c *noopCache) DeletePluginMetadata(pm *PluginMetadataRow) error {
        return nil
 }
 
diff --git a/internal/adc/cache/schema.go b/internal/adc/cache/schema.go
index 97c32deb..3c3a42e3 100644
--- a/internal/adc/cache/schema.go
+++ b/internal/adc/cache/schema.go
@@ -86,11 +86,20 @@ var (
                                                Unique:  true,
                                                Indexer: 
&memdb.StringFieldIndex{Field: "ID"},
                                        },
-                                       KindLabelIndex: {
-                                               Name:         KindLabelIndex,
-                                               Unique:       false,
-                                               AllowMissing: true,
-                                               Indexer:      &KindLabelIndexer,
+                                       OwnerIndex: {
+                                               Name:    OwnerIndex,
+                                               Unique:  false,
+                                               Indexer: &ownerIndexer{},
+                                       },
+                               },
+                       },
+                       "plugin_metadata": {
+                               Name: "plugin_metadata",
+                               Indexes: map[string]*memdb.IndexSchema{
+                                       "id": {
+                                               Name:    "id",
+                                               Unique:  true,
+                                               Indexer: 
&memdb.StringFieldIndex{Field: "ID"},
                                        },
                                },
                        },
diff --git a/internal/adc/cache/store.go b/internal/adc/cache/store.go
index 17a8a6d6..fb4e5192 100644
--- a/internal/adc/cache/store.go
+++ b/internal/adc/cache/store.go
@@ -19,11 +19,9 @@ package cache
 
 import (
        "cmp"
-       "fmt"
        "sync"
 
        "github.com/go-logr/logr"
-       "github.com/google/uuid"
 
        adctypes "github.com/apache/apisix-ingress-controller/api/adc"
        "github.com/apache/apisix-ingress-controller/internal/controller/label"
@@ -31,14 +29,14 @@ import (
 )
 
 type Store struct {
-       cacheMap          map[string]Cache
-       pluginMetadataMap map[string]adctypes.PluginMetadata
+       cacheMap map[string]Cache
 
        sync.Mutex
        log logr.Logger
 }
 
-// Entity is a top-level ADC resource a cacheKey holds: a service, ssl or 
consumer.
+// Entity is a top-level ADC resource a cacheKey holds: a service, ssl, 
consumer,
+// global_rule or plugin_metadata.
 type Entity struct {
        Type string
        ID   string
@@ -54,12 +52,23 @@ type Entity struct {
 
 func NewStore(log logr.Logger) *Store {
        return &Store{
-               cacheMap:          make(map[string]Cache),
-               pluginMetadataMap: make(map[string]adctypes.PluginMetadata),
-               log:               log.WithName("store"),
+               cacheMap: make(map[string]Cache),
+               log:      log.WithName("store"),
        }
 }
 
+func (s *Store) cacheFor(name string) (Cache, error) {
+       if c, ok := s.cacheMap[name]; ok {
+               return c, nil
+       }
+       db, err := NewMemDBCache()
+       if err != nil {
+               return nil, err
+       }
+       s.cacheMap[name] = db
+       return db, nil
+}
+
 func ownerFromLabels(labels map[string]string) types.NamespacedNameKind {
        return types.NamespacedNameKind{
                Kind:      labels[label.LabelKind],
@@ -68,6 +77,15 @@ func ownerFromLabels(labels map[string]string) 
types.NamespacedNameKind {
        }
 }
 
+// gatewayProxyOf returns the GatewayProxy a cacheKey names.
+func gatewayProxyOf(name string) (types.NamespacedNameKind, bool) {
+       var gatewayProxy types.NamespacedNameKind
+       if err := gatewayProxy.FromString(name); err != nil {
+               return types.NamespacedNameKind{}, false
+       }
+       return gatewayProxy, true
+}
+
 func childrenOf(service *adctypes.Service, owner types.NamespacedNameKind) 
[]Entity {
        children := make([]Entity, 0, 
len(service.Routes)+len(service.StreamRoutes))
        for _, route := range service.Routes {
@@ -79,17 +97,71 @@ func childrenOf(service *adctypes.Service, owner 
types.NamespacedNameKind) []Ent
        return children
 }
 
+// routeOwner finds the Kubernetes resource that produced the route id, by 
scanning
+// every service the cacheKey holds. A route's own labels can differ from its 
service's
+// (a traffic-split service can combine rules several ApisixRoutes each 
contributed),
+// so this can't reuse the service's own KindLabelSelector match.
+func routeOwner(targetCache Cache, id string) (types.NamespacedNameKind, bool) 
{
+       services, err := targetCache.ListServices()
+       if err != nil {
+               return types.NamespacedNameKind{}, false
+       }
+       for _, service := range services {
+               for _, route := range service.Routes {
+                       if route.ID == id {
+                               return ownerFromLabels(route.GetLabels()), true
+                       }
+               }
+       }
+       return types.NamespacedNameKind{}, false
+}
+
+// setGlobalRules is Insert and SetGlobalRules' shared implementation. Callers 
must
+// already hold s.Lock.
+func (s *Store) setGlobalRules(targetCache Cache, owner 
types.NamespacedNameKind, plugins adctypes.GlobalRule) error {
+       rows, err := targetCache.ListGlobalRules(&OwnerSelector{Owner: owner})
+       if err != nil {
+               return err
+       }
+       for _, row := range rows {
+               if err := targetCache.DeleteGlobalRule(row); err != nil {
+                       return err
+               }
+       }
+       for pluginName, config := range plugins {
+               if err := targetCache.InsertGlobalRule(&GlobalRuleRow{ID: 
pluginName, Owner: owner, Config: config}); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// setPluginMetadata is Insert and SetPluginMetadata's shared implementation. 
Callers
+// must already hold s.Lock.
+func (s *Store) setPluginMetadata(targetCache Cache, metadata 
adctypes.PluginMetadata) error {
+       rows, err := targetCache.ListPluginMetadata()
+       if err != nil {
+               return err
+       }
+       for _, row := range rows {
+               if err := targetCache.DeletePluginMetadata(row); err != nil {
+                       return err
+               }
+       }
+       for pluginName, config := range metadata {
+               if err := 
targetCache.InsertPluginMetadata(&PluginMetadataRow{ID: pluginName, Config: 
config}); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
 func (s *Store) Insert(name string, resourceTypes []string, resources 
*adctypes.Resources, Labels map[string]string) error {
        s.Lock()
        defer s.Unlock()
-       targetCache, ok := s.cacheMap[name]
-       if !ok {
-               db, err := NewMemDBCache()
-               if err != nil {
-                       return err
-               }
-               s.cacheMap[name] = db
-               targetCache = s.cacheMap[name]
+       targetCache, err := s.cacheFor(name)
+       if err != nil {
+               return err
        }
        s.log.V(1).Info("Inserting resources into cache", "name", name, 
"resourceTypes", resourceTypes, "Labels", Labels)
        selector := &KindLabelSelector{
@@ -146,34 +218,13 @@ func (s *Store) Insert(name string, resourceTypes 
[]string, resources *adctypes.
                                }
                        }
                case adctypes.TypeGlobalRule:
-                       // List existing global rules that match the selector
-                       globalRules, err := 
targetCache.ListGlobalRules(selector)
-                       if err != nil {
+                       if err := s.setGlobalRules(targetCache, 
ownerFromLabels(Labels), resources.GlobalRules); err != nil {
                                return err
                        }
-                       // Delete existing matching global rules
-                       for _, globalRule := range globalRules {
-                               if err := 
targetCache.DeleteGlobalRule(globalRule); err != nil {
-                                       return err
-                               }
-                       }
-                       // Convert GlobalRule (Plugins) to GlobalRuleItem and 
insert
-                       if len(resources.GlobalRules) > 0 {
-                               id := name + "-" + uuid.NewString()
-                               globalRuleItem := &adctypes.GlobalRuleItem{
-                                       Metadata: adctypes.Metadata{
-                                               ID:     id,
-                                               Name:   id,
-                                               Labels: Labels,
-                                       },
-                                       Plugins: 
adctypes.Plugins(resources.GlobalRules),
-                               }
-                               if err := 
targetCache.InsertGlobalRule(globalRuleItem); err != nil {
-                                       return err
-                               }
-                       }
                case adctypes.TypePluginMetadata:
-                       s.pluginMetadataMap[name] = resources.PluginMetadata
+                       if err := s.setPluginMetadata(targetCache, 
resources.PluginMetadata); err != nil {
+                               return err
+                       }
                default:
                        continue
                }
@@ -226,17 +277,13 @@ func (s *Store) Delete(name string, resourceTypes 
[]string, Labels map[string]st
                                }
                        }
                case adctypes.TypeGlobalRule:
-                       globalRules, err := 
targetCache.ListGlobalRules(selector)
-                       if err != nil {
-                               s.log.Error(err, "failed to list global rules")
-                       }
-                       for _, globalRule := range globalRules {
-                               if err := 
targetCache.DeleteGlobalRule(globalRule); err != nil {
-                                       s.log.Error(err, "failed to delete 
global rule", "global rule", globalRule.ID)
-                               }
+                       if err := s.setGlobalRules(targetCache, 
ownerFromLabels(Labels), nil); err != nil {
+                               s.log.Error(err, "failed to delete global 
rules")
                        }
                case adctypes.TypePluginMetadata:
-                       delete(s.pluginMetadataMap, name)
+                       if err := s.setPluginMetadata(targetCache, nil); err != 
nil {
+                               s.log.Error(err, "failed to delete plugin 
metadata")
+                       }
                }
        }
        if len(resourceTypes) == 0 {
@@ -252,22 +299,21 @@ func (s *Store) GetResources(name string) 
(*adctypes.Resources, error) {
        if !ok {
                return &adctypes.Resources{}, nil
        }
-       var globalrule adctypes.GlobalRule
-       var metadata adctypes.PluginMetadata
-       // Get all global rules from cache and merge them
-       globalRuleItems, _ := targetCache.ListGlobalRules()
-       if len(globalRuleItems) > 0 {
-               merged := make(adctypes.Plugins)
-               for _, item := range globalRuleItems {
-                       for k, v := range item.Plugins {
-                               merged[k] = v
-                       }
+       var globalRules adctypes.GlobalRule
+       globalRuleRows, _ := targetCache.ListGlobalRules()
+       if len(globalRuleRows) > 0 {
+               globalRules = make(adctypes.GlobalRule, len(globalRuleRows))
+               for _, row := range globalRuleRows {
+                       globalRules[row.ID] = row.Config
                }
-               globalrule = adctypes.GlobalRule(merged)
        }
-       s.log.V(1).Info("GetResources fetched global rule items", "itemCount", 
len(globalRuleItems), "pluginCount", len(globalrule))
-       if meta, ok := s.pluginMetadataMap[name]; ok {
-               metadata = meta.DeepCopy()
+       var pluginMetadata adctypes.PluginMetadata
+       pluginMetadataRows, _ := targetCache.ListPluginMetadata()
+       if len(pluginMetadataRows) > 0 {
+               pluginMetadata = make(adctypes.PluginMetadata, 
len(pluginMetadataRows))
+               for _, row := range pluginMetadataRows {
+                       pluginMetadata[row.ID] = row.Config
+               }
        }
        consumers, _ := targetCache.ListConsumers()
        services, _ := targetCache.ListServices()
@@ -276,89 +322,42 @@ func (s *Store) GetResources(name string) 
(*adctypes.Resources, error) {
                Consumers:      consumers,
                Services:       services,
                SSLs:           ssls,
-               GlobalRules:    globalrule,
-               PluginMetadata: metadata,
+               GlobalRules:    globalRules,
+               PluginMetadata: pluginMetadata,
        }, nil
 }
 
-func (s *Store) ListGlobalRules(name string) ([]*adctypes.GlobalRuleItem, 
error) {
+// SetGlobalRules replaces the global_rules plugins owner declares in the 
cacheKey name
+// with plugins; an empty plugins removes all of them. A plugin name can only 
be
+// configured once, so another owner's plugin of the same name is overwritten: 
which
+// declaration wins is undefined.
+func (s *Store) SetGlobalRules(name string, owner types.NamespacedNameKind, 
plugins adctypes.GlobalRule) error {
        s.Lock()
        defer s.Unlock()
-       targetCache, ok := s.cacheMap[name]
-       if !ok {
-               return nil, fmt.Errorf("cache not found for name: %s", name)
-       }
-       globalRules, err := targetCache.ListGlobalRules()
+       targetCache, err := s.cacheFor(name)
        if err != nil {
-               return nil, fmt.Errorf("failed to list global rules: %w", err)
+               return err
        }
-       return globalRules, nil
+       return s.setGlobalRules(targetCache, owner, plugins)
 }
 
-func (s *Store) GetResourceLabel(name, resourceType string, id string) 
(map[string]string, error) {
+// SetPluginMetadata replaces all plugin_metadata the cacheKey name holds.
+func (s *Store) SetPluginMetadata(name string, metadata 
adctypes.PluginMetadata) error {
        s.Lock()
        defer s.Unlock()
-       targetCache, ok := s.cacheMap[name]
-       if !ok {
-               return nil, fmt.Errorf("cache not found for name: %s", name)
-       }
-       switch resourceType {
-       case adctypes.TypeService:
-               service, err := targetCache.GetService(id)
-               if err != nil {
-                       return nil, fmt.Errorf("failed to get service: %w", err)
-               }
-               return service.Labels, nil
-       case adctypes.TypeRoute:
-               services, err := targetCache.ListServices()
-               if err != nil {
-                       return nil, fmt.Errorf("failed to list services: %w", 
err)
-               }
-               for _, service := range services {
-                       for _, route := range service.Routes {
-                               if route.ID == id {
-                                       // Return labels from the service that 
contains the route
-                                       return route.GetLabels(), nil
-                               }
-                       }
-               }
-               return nil, fmt.Errorf("route not found: %s", id)
-       case adctypes.TypeSSL:
-               ssl, err := targetCache.GetSSL(id)
-               if err != nil {
-                       return nil, err
-               }
-               if ssl != nil {
-                       return ssl.GetLabels(), nil
-               }
-       case adctypes.TypeConsumer:
-               consumer, err := targetCache.GetConsumer(id)
-               if err != nil {
-                       return nil, err
-               }
-               if consumer != nil {
-                       return consumer.Labels, nil
-               }
-       case adctypes.TypeGlobalRule:
-               globalRule, err := targetCache.GetGlobalRule(id)
-               if err != nil {
-                       return nil, err
-               }
-               if globalRule != nil {
-                       return globalRule.GetLabels(), nil
-               }
-       default:
-               return nil, fmt.Errorf("unknown resource type: %s", 
resourceType)
+       targetCache, err := s.cacheFor(name)
+       if err != nil {
+               return err
        }
-       return nil, nil
+       return s.setPluginMetadata(targetCache, metadata)
 }
 
 // Lookup finds the top-level entity of resourceType and id the cacheKey name 
holds,
 // along with the Kubernetes resource that produced it, read straight from the 
entity's
 // own stored labels (the same ones KindLabelSelector matches Insert/Delete 
against, so
-// there is exactly one place this can ever disagree with what a selector 
would find). It
-// covers service, ssl and consumer; global_rule and plugin_metadata still 
only expose
-// GetResourceLabel, until their own storage grows the same per-entity owner 
this does.
+// there is exactly one place this can ever disagree with what a selector 
would find). A
+// route is not top-level, but is looked up the same way pending its own 
nested-entity
+// tracking.
 func (s *Store) Lookup(name, resourceType, id string) (Entity, bool) {
        s.Lock()
        defer s.Unlock()
@@ -385,13 +384,34 @@ func (s *Store) Lookup(name, resourceType, id string) 
(Entity, bool) {
                        return Entity{}, false
                }
                return Entity{Type: resourceType, ID: id, Name: id, Owner: 
ownerFromLabels(consumer.GetLabels())}, true
+       case adctypes.TypeRoute:
+               owner, ok := routeOwner(targetCache, id)
+               if !ok {
+                       return Entity{}, false
+               }
+               return Entity{Type: resourceType, ID: id, Name: id, Owner: 
owner}, true
+       case adctypes.TypeGlobalRule:
+               row, err := targetCache.GetGlobalRule(id)
+               if err != nil {
+                       return Entity{}, false
+               }
+               return Entity{Type: resourceType, ID: id, Name: id, Owner: 
row.Owner}, true
+       case adctypes.TypePluginMetadata:
+               if _, err := targetCache.GetPluginMetadata(id); err != nil {
+                       return Entity{}, false
+               }
+               gatewayProxy, ok := gatewayProxyOf(name)
+               if !ok {
+                       return Entity{}, false
+               }
+               return Entity{Type: resourceType, ID: id, Name: id, Owner: 
gatewayProxy}, true
        }
        return Entity{}, false
 }
 
-// OwnedEntities lists every service, ssl and consumer owner produced in the 
cacheKey
-// name, via the same KindLabelSelector index Insert and Delete already use to 
find an
-// owner's resources. See Lookup for why global_rule and plugin_metadata are 
absent.
+// OwnedEntities lists every top-level entity owner produced in the cacheKey 
name, via
+// the same KindLabelSelector index Insert and Delete already use for service, 
ssl and
+// consumer.
 func (s *Store) OwnedEntities(name string, owner types.NamespacedNameKind) 
[]Entity {
        s.Lock()
        defer s.Unlock()
@@ -400,28 +420,36 @@ func (s *Store) OwnedEntities(name string, owner 
types.NamespacedNameKind) []Ent
                return nil
        }
        selector := &KindLabelSelector{Kind: owner.Kind, Namespace: 
owner.Namespace, Name: owner.Name}
+       services, _ := targetCache.ListServices(selector)
+       ssls, _ := targetCache.ListSSL(selector)
+       consumers, _ := targetCache.ListConsumers(selector)
+       globalRules, _ := targetCache.ListGlobalRules(&OwnerSelector{Owner: 
owner})
+       var pluginMetadata []*PluginMetadataRow
+       if gatewayProxy, ok := gatewayProxyOf(name); ok && gatewayProxy == 
owner {
+               pluginMetadata, _ = targetCache.ListPluginMetadata()
+       }
 
-       var entities []Entity
-       if services, err := targetCache.ListServices(selector); err == nil {
-               for _, service := range services {
-                       entities = append(entities, Entity{
-                               Type:     adctypes.TypeService,
-                               ID:       service.ID,
-                               Name:     cmp.Or(service.Name, service.ID),
-                               Owner:    owner,
-                               Children: childrenOf(service, owner),
-                       })
-               }
+       entities := make([]Entity, 0, 
len(services)+len(ssls)+len(consumers)+len(globalRules)+len(pluginMetadata))
+       for _, service := range services {
+               entities = append(entities, Entity{
+                       Type:     adctypes.TypeService,
+                       ID:       service.ID,
+                       Name:     cmp.Or(service.Name, service.ID),
+                       Owner:    owner,
+                       Children: childrenOf(service, owner),
+               })
        }
-       if ssls, err := targetCache.ListSSL(selector); err == nil {
-               for _, ssl := range ssls {
-                       entities = append(entities, Entity{Type: 
adctypes.TypeSSL, ID: ssl.ID, Name: ssl.ID, Owner: owner})
-               }
+       for _, ssl := range ssls {
+               entities = append(entities, Entity{Type: adctypes.TypeSSL, ID: 
ssl.ID, Name: ssl.ID, Owner: owner})
        }
-       if consumers, err := targetCache.ListConsumers(selector); err == nil {
-               for _, consumer := range consumers {
-                       entities = append(entities, Entity{Type: 
adctypes.TypeConsumer, ID: consumer.Username, Name: consumer.Username, Owner: 
owner})
-               }
+       for _, consumer := range consumers {
+               entities = append(entities, Entity{Type: adctypes.TypeConsumer, 
ID: consumer.Username, Name: consumer.Username, Owner: owner})
+       }
+       for _, row := range globalRules {
+               entities = append(entities, Entity{Type: 
adctypes.TypeGlobalRule, ID: row.ID, Name: row.ID, Owner: owner})
+       }
+       for _, row := range pluginMetadata {
+               entities = append(entities, Entity{Type: 
adctypes.TypePluginMetadata, ID: row.ID, Name: row.ID, Owner: owner})
        }
        return entities
 }
diff --git a/internal/adc/cache/store_test.go b/internal/adc/cache/store_test.go
index 288c6d48..1fdd46b7 100644
--- a/internal/adc/cache/store_test.go
+++ b/internal/adc/cache/store_test.go
@@ -31,6 +31,8 @@ import (
 
 const configName = "GatewayProxy/ns/gp"
 
+var gatewayProxy = types.NamespacedNameKind{Kind: types.KindGatewayProxy, 
Namespace: "ns", Name: "gp"}
+
 func ownerNamed(kind, name string) types.NamespacedNameKind {
        return types.NamespacedNameKind{Kind: kind, Namespace: "ns", Name: name}
 }
@@ -55,11 +57,14 @@ func TestLookupFindsTheOwnerOfEveryTopLevelType(t 
*testing.T) {
        route := ownerNamed(types.KindApisixRoute, "route")
        tls := ownerNamed(types.KindApisixTls, "tls")
        consumerOwner := ownerNamed(types.KindConsumer, "consumer")
+       globalRule := ownerNamed(types.KindApisixGlobalRule, "global")
 
        s := NewStore(logr.Discard())
        require.NoError(t, s.Insert(configName, []string{adctypes.TypeService}, 
&adctypes.Resources{Services: []*adctypes.Service{service("svc", route)}}, 
labelsOf(route)))
        require.NoError(t, s.Insert(configName, []string{adctypes.TypeSSL}, 
&adctypes.Resources{SSLs: []*adctypes.SSL{ssl("ssl", tls)}}, labelsOf(tls)))
        require.NoError(t, s.Insert(configName, 
[]string{adctypes.TypeConsumer}, &adctypes.Resources{Consumers: 
[]*adctypes.Consumer{consumer("alice", consumerOwner)}}, 
labelsOf(consumerOwner)))
+       require.NoError(t, s.SetGlobalRules(configName, globalRule, 
adctypes.GlobalRule{"prometheus": map[string]any{}}))
+       require.NoError(t, s.SetPluginMetadata(configName, 
adctypes.PluginMetadata{"http-logger": map[string]any{}}))
 
        cases := []struct {
                resourceType, id, name string
@@ -68,6 +73,8 @@ func TestLookupFindsTheOwnerOfEveryTopLevelType(t *testing.T) 
{
                {adctypes.TypeService, "svc", "name-svc", route},
                {adctypes.TypeSSL, "ssl", "ssl", tls},
                {adctypes.TypeConsumer, "alice", "alice", consumerOwner},
+               {adctypes.TypeGlobalRule, "prometheus", "prometheus", 
globalRule},
+               {adctypes.TypePluginMetadata, "http-logger", "http-logger", 
gatewayProxy},
        }
        for _, tc := range cases {
                t.Run(tc.resourceType, func(t *testing.T) {
@@ -80,20 +87,72 @@ func TestLookupFindsTheOwnerOfEveryTopLevelType(t 
*testing.T) {
 
        _, ok := s.Lookup(configName, adctypes.TypeService, "missing")
        assert.False(t, ok)
+       _, ok = s.Lookup(configName, adctypes.TypePluginMetadata, "missing")
+       assert.False(t, ok)
        _, ok = s.Lookup("GatewayProxy/ns/other", adctypes.TypeService, "svc")
        assert.False(t, ok)
 }
 
-// TestLookupHasNoOpinionOnGlobalRuleOrPluginMetadataYet documents the current 
boundary:
-// their storage carries no owner yet, so Lookup can't answer for them.
-func TestLookupHasNoOpinionOnGlobalRuleOrPluginMetadataYet(t *testing.T) {
+// TestLookupFindsARouteByItsOwnLabels covers the one nested type Lookup 
already
+// answers for: a route's own owner, which the service holding it doesn't 
always share
+// (e.g. a traffic-split service combining rules from several ApisixRoutes).
+func TestLookupFindsARouteByItsOwnLabels(t *testing.T) {
+       svcOwner := ownerNamed(types.KindApisixRoute, "service-writer")
+       routeOwner := ownerNamed(types.KindApisixRoute, "route-writer")
        s := NewStore(logr.Discard())
-       require.NoError(t, s.Insert(configName, 
[]string{adctypes.TypeGlobalRule}, &adctypes.Resources{GlobalRules: 
adctypes.GlobalRule{"prometheus": map[string]any{}}}, 
labelsOf(ownerNamed(types.KindApisixGlobalRule, "global"))))
+       withRoute := service("svc", svcOwner)
+       withRoute.Routes = []*adctypes.Route{{Metadata: adctypes.Metadata{ID: 
"r1", Labels: labelsOf(routeOwner)}}}
+       require.NoError(t, s.Insert(configName, []string{adctypes.TypeService}, 
&adctypes.Resources{Services: []*adctypes.Service{withRoute}}, 
labelsOf(svcOwner)))
+
+       entity, ok := s.Lookup(configName, adctypes.TypeRoute, "r1")
+       require.True(t, ok)
+       assert.Equal(t, routeOwner, entity.Owner, "the route's own owner, not 
the service's")
 
-       _, ok := s.Lookup(configName, adctypes.TypeGlobalRule, "prometheus")
+       _, ok = s.Lookup(configName, adctypes.TypeRoute, "missing")
        assert.False(t, ok)
 }
 
+func TestSetGlobalRulesReplacesOnlyThatOwnersPlugins(t *testing.T) {
+       globalRule := ownerNamed(types.KindApisixGlobalRule, "global")
+       s := NewStore(logr.Discard())
+       require.NoError(t, s.SetGlobalRules(configName, gatewayProxy, 
adctypes.GlobalRule{"cors": map[string]any{"a": "b"}}))
+       require.NoError(t, s.SetGlobalRules(configName, globalRule, 
adctypes.GlobalRule{"prometheus": map[string]any{}, "old": map[string]any{}}))
+       require.NoError(t, s.SetGlobalRules(configName, globalRule, 
adctypes.GlobalRule{"prometheus": map[string]any{}}))
+
+       resources, err := s.GetResources(configName)
+       require.NoError(t, err)
+       assert.Equal(t, adctypes.GlobalRule{"cors": map[string]any{"a": "b"}, 
"prometheus": map[string]any{}}, resources.GlobalRules)
+
+       require.NoError(t, s.SetGlobalRules(configName, globalRule, nil))
+       resources, err = s.GetResources(configName)
+       require.NoError(t, err)
+       assert.Equal(t, adctypes.GlobalRule{"cors": map[string]any{"a": "b"}}, 
resources.GlobalRules)
+}
+
+func TestSetGlobalRulesOfTheSameNameOverwritesAndAttributesToTheLastWriter(t 
*testing.T) {
+       globalRule := ownerNamed(types.KindApisixGlobalRule, "global")
+       s := NewStore(logr.Discard())
+       require.NoError(t, s.SetGlobalRules(configName, gatewayProxy, 
adctypes.GlobalRule{"prometheus": map[string]any{"from": "gp"}}))
+       require.NoError(t, s.SetGlobalRules(configName, globalRule, 
adctypes.GlobalRule{"prometheus": map[string]any{"from": "agr"}}))
+
+       resources, err := s.GetResources(configName)
+       require.NoError(t, err)
+       entity, ok := s.Lookup(configName, adctypes.TypeGlobalRule, 
"prometheus")
+       require.True(t, ok)
+       assert.Equal(t, map[string]any{"from": "agr"}, 
resources.GlobalRules["prometheus"])
+       assert.Equal(t, globalRule, entity.Owner, "what is pushed and who it is 
attributed to always agree")
+}
+
+func TestSetPluginMetadataReplacesEverything(t *testing.T) {
+       s := NewStore(logr.Discard())
+       require.NoError(t, s.SetPluginMetadata(configName, 
adctypes.PluginMetadata{"old": map[string]any{}}))
+       require.NoError(t, s.SetPluginMetadata(configName, 
adctypes.PluginMetadata{"new": map[string]any{}}))
+
+       resources, err := s.GetResources(configName)
+       require.NoError(t, err)
+       assert.Equal(t, adctypes.PluginMetadata{"new": map[string]any{}}, 
resources.PluginMetadata)
+}
+
 // TestLookupReadsTheEntitysOwnLabelsNotInsertsArgument covers why Lookup 
can't source
 // the owner from anywhere but the entity's own stored labels: those are also 
what
 // KindLabelSelector matches Delete and a future Insert against, so this is 
the only
@@ -163,3 +222,19 @@ func TestOwnedEntities(t *testing.T) {
 
        assert.Empty(t, s.OwnedEntities(configName, 
ownerNamed(types.KindApisixRoute, "nobody")))
 }
+
+func TestOwnedEntitiesIncludesGlobalRulesAndPluginMetadataOfTheGatewayProxy(t 
*testing.T) {
+       globalRule := ownerNamed(types.KindApisixGlobalRule, "global")
+       s := NewStore(logr.Discard())
+       require.NoError(t, s.SetGlobalRules(configName, gatewayProxy, 
adctypes.GlobalRule{"cors": map[string]any{}}))
+       require.NoError(t, s.SetGlobalRules(configName, globalRule, 
adctypes.GlobalRule{"prometheus": map[string]any{}}))
+       require.NoError(t, s.SetPluginMetadata(configName, 
adctypes.PluginMetadata{"http-logger": map[string]any{}}))
+
+       gpEntities := s.OwnedEntities(configName, gatewayProxy)
+       assert.Len(t, gpEntities, 2, "the GatewayProxy's own global rule and 
the cacheKey's plugin metadata")
+
+       agrEntities := s.OwnedEntities(configName, globalRule)
+       require.Len(t, agrEntities, 1)
+       assert.Equal(t, adctypes.TypeGlobalRule, agrEntities[0].Type)
+       assert.Equal(t, "prometheus", agrEntities[0].ID)
+}
diff --git a/internal/provider/apisix/status.go 
b/internal/provider/apisix/status.go
index 0aeb58ce..40f3fc22 100644
--- a/internal/provider/apisix/status.go
+++ b/internal/provider/apisix/status.go
@@ -30,7 +30,6 @@ import (
        adctypes "github.com/apache/apisix-ingress-controller/api/adc"
        apiv1alpha1 "github.com/apache/apisix-ingress-controller/api/v1alpha1"
        apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
-       "github.com/apache/apisix-ingress-controller/internal/controller/label"
        "github.com/apache/apisix-ingress-controller/internal/controller/status"
        cutils 
"github.com/apache/apisix-ingress-controller/internal/controller/utils"
        "github.com/apache/apisix-ingress-controller/internal/types"
@@ -122,23 +121,13 @@ func (d *apisixProvider) classifySyncResult(
 
                        anyUnattributed := false
                        for _, syncStatus := range addrErr.FailedStatuses {
-                               if syncStatus.Event.ResourceType == "" {
+                               entity, ok := d.store.Lookup(configName, 
syncStatus.Event.ResourceType, syncStatus.Event.ResourceID)
+                               if !ok {
                                        anyUnattributed = true
                                        continue
                                }
-                               labels, err := 
d.store.GetResourceLabel(configName, syncStatus.Event.ResourceType, 
syncStatus.Event.ResourceID)
-                               if err != nil {
-                                       d.log.Error(err, "failed to get 
resource label",
-                                               "configName", configName, 
"resourceType", syncStatus.Event.ResourceType, "id", 
syncStatus.Event.ResourceID)
-                                       continue
-                               }
-                               resourceKey := types.NamespacedNameKind{
-                                       Name:      labels[label.LabelName],
-                                       Namespace: labels[label.LabelNamespace],
-                                       Kind:      labels[label.LabelKind],
-                               }
                                msg := fmt.Sprintf("ServerAddr: %s, Error: %s", 
addrErr.ServerAddr, syncStatus.Reason)
-                               resourceFailures[resourceKey] = 
append(resourceFailures[resourceKey], msg)
+                               resourceFailures[entity.Owner] = 
append(resourceFailures[entity.Owner], msg)
                        }
                        if anyUnattributed && endpointMsg == "" {
                                gatewayProxyMsgs = append(gatewayProxyMsgs, 
addrErr.Error())
diff --git a/internal/provider/apisix/status_test.go 
b/internal/provider/apisix/status_test.go
index 81fa567d..fa5f343c 100644
--- a/internal/provider/apisix/status_test.go
+++ b/internal/provider/apisix/status_test.go
@@ -271,18 +271,14 @@ func 
TestClassifySyncResultEndpointFailuresGoToGatewayProxy(t *testing.T) {
 func TestClassifySyncResultAttributesFailedStatusesToTheirResource(t 
*testing.T) {
        d := &apisixProvider{log: logr.Discard(), store: 
cache.NewStore(logr.Discard())}
        const configName = "GatewayProxy/ns/gp"
+       owner := map[string]string{
+               label.LabelKind:      "ApisixRoute",
+               label.LabelName:      "route1",
+               label.LabelNamespace: "ns1",
+       }
        if err := d.store.Insert(configName, []string{adctypes.TypeService}, 
&adctypes.Resources{
-               Services: []*adctypes.Service{{
-                       Metadata: adctypes.Metadata{
-                               ID: "svc1",
-                               Labels: map[string]string{
-                                       label.LabelKind:      "ApisixRoute",
-                                       label.LabelName:      "route1",
-                                       label.LabelNamespace: "ns1",
-                               },
-                       },
-               }},
-       }, nil); err != nil {
+               Services: []*adctypes.Service{{Metadata: adctypes.Metadata{ID: 
"svc1", Labels: owner}}},
+       }, owner); err != nil {
                t.Fatalf("seeding the store: %v", err)
        }
 
@@ -319,18 +315,14 @@ func 
TestClassifySyncResultReportsEndpointStatusesEvenOnAFullyAttributedAddrErr(
        // endpoint failure too.
        d := &apisixProvider{log: logr.Discard(), store: 
cache.NewStore(logr.Discard())}
        const configName = "GatewayProxy/ns/gp"
+       owner := map[string]string{
+               label.LabelKind:      "ApisixRoute",
+               label.LabelName:      "route1",
+               label.LabelNamespace: "ns1",
+       }
        if err := d.store.Insert(configName, []string{adctypes.TypeService}, 
&adctypes.Resources{
-               Services: []*adctypes.Service{{
-                       Metadata: adctypes.Metadata{
-                               ID: "svc1",
-                               Labels: map[string]string{
-                                       label.LabelKind:      "ApisixRoute",
-                                       label.LabelName:      "route1",
-                                       label.LabelNamespace: "ns1",
-                               },
-                       },
-               }},
-       }, nil); err != nil {
+               Services: []*adctypes.Service{{Metadata: adctypes.Metadata{ID: 
"svc1", Labels: owner}}},
+       }, owner); err != nil {
                t.Fatalf("seeding the store: %v", err)
        }
 
@@ -367,7 +359,7 @@ func 
TestClassifySyncResultReportsEndpointStatusesEvenOnAFullyAttributedAddrErr(
 func 
TestClassifySyncResultFallsBackToGatewayProxyWhenAFailedStatusHasNoResourceAttribution(t
 *testing.T) {
        // apisix-standalone: FailedStatuses can be non-empty yet carry no 
Event to resolve a
        // resource from at all, the whole addrErr is then a GatewayProxy-level 
signal.
-       d := &apisixProvider{log: logr.Discard()}
+       d := &apisixProvider{log: logr.Discard(), store: 
cache.NewStore(logr.Discard())}
        execErrs := types.ADCExecutionErrors{Errors: []types.ADCExecutionError{{
                Name: "GatewayProxy/ns/gp",
                FailedErrors: []types.ADCExecutionServerAddrError{{

Reply via email to