Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/dghubble/trie v0.1.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/net v0.41.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/dghubble/trie v0.1.0 h1:kJnjBLFFElBwS60N4tkPvnLhnpcDxbBjIulgI8CpNGM=
github.com/dghubble/trie v0.1.0/go.mod h1:sOmnzfBNH7H92ow2292dDFWNsVQuh/izuD7otCYb1ak=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
Expand Down
124 changes: 124 additions & 0 deletions pkg/ingressCache/ingressCache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
Copyright 2019 Iguazio Systems Ltd.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Copyright 2019 Iguazio Systems Ltd.
Copyright 2025 Iguazio Systems Ltd.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here - CR comment - Copyright


Licensed under the Apache License, Version 2.0 (the "License") with
an addition restriction as set forth herein. You may not use this
file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.

In addition, you may not use the software for any purposes that are
illegal under applicable law, and the grant of the foregoing license
under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/

package ingresscache

import (
"sync"

"github.com/nuclio/errors"
"github.com/nuclio/logger"
)

type IngressHostsTree interface {
SetFunctionName(path string, function string) error // will overwrite existing values if exists
DeleteFunctionName(path string, function string) error
GetFunctionName(path string) ([]string, error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe remove name from SetFunctionName and others? So when we move to typed trie values (not [string]), it will return either canaryFunction or singleFunction

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should keep using functionName — it's a commonly used convention and clearly describes the value the trie holds.
But as @TomerShor recommended here, I will change the GetFunctionName specifically.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weilerN I think we will need to rename when code is moved from using []string to structs, which will be with function prefix in the name (e.g canaryFunction, singleFunction). Though naming can be reconsidered in the following PR

IsEmpty() bool
}

type IngressHostCache interface {
Set(host string, path string, function string) error // will overwrite existing values if exists
Delete(host string, path string, function string) error
Get(host string, path string) ([]string, error)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top lvl entity up

Suggested change
type IngressHostsTree interface {
SetFunctionName(path string, function string) error // will overwrite existing values if exists
DeleteFunctionName(path string, function string) error
GetFunctionName(path string) ([]string, error)
IsEmpty() bool
}
type IngressHostCache interface {
Set(host string, path string, function string) error // will overwrite existing values if exists
Delete(host string, path string, function string) error
Get(host string, path string) ([]string, error)
}
type IngressHostCache interface {
Set(host string, path string, function string) error // will overwrite existing values if exists
Delete(host string, path string, function string) error
Get(host string, path string) ([]string, error)
}
type IngressHostsTree interface {
SetFunctionName(path string, function string) error // will overwrite existing values if exists
DeleteFunctionName(path string, function string) error
GetFunctionName(path string) ([]string, error)
IsEmpty() bool
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


type IngressCache struct {
syncMap *sync.Map
logger logger.Logger
}

func NewIngressCache(logger logger.Logger) *IngressCache {
return &IngressCache{
syncMap: &sync.Map{},
logger: logger,
}
}

func (ic *IngressCache) Set(host, path, function string) error {
urlTree, exists := ic.syncMap.Load(host)
if !exists {
urlTree = NewSafeTrie()
ic.syncMap.Store(host, urlTree)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ic.syncMap.LoadOrStore() instead

Copy link
Collaborator Author

@weilerN weilerN Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid using syncMap.Delete on every failure, I chose to keep the result from syncMap.Load and only store it at the end of the positive flow.

Fixed here - CR comment - improve usage of syncMap and slice

ingressHostsTree, ok := urlTree.(IngressHostsTree)
if !ok {
// remove the host from the cache when it's a new entry
if !exists {
ic.syncMap.Delete(host)
}
return errors.Errorf("cache set failed: invalid path tree value: got: %t", urlTree)
}

if err := ingressHostsTree.SetFunctionName(path, function); err != nil {
// remove the host from the cache when it's a new entry
if !exists {
ic.syncMap.Delete(host)
}
return errors.Wrap(err, "cache set failed")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even more explicit:

Suggested change
return errors.Wrap(err, "cache set failed")
return errors.Wrap(err, "Failed to set function name in the ingress host tree")

The caller of Set will return an error Failed to set function in cache.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here

}
return nil
}

func (ic *IngressCache) Delete(host, path, function string) error {
urlTree, exists := ic.syncMap.Load(host)
if !exists {
ic.logger.Debug("cache delete: host not found")
return nil
}

ingressHostsTree, ok := urlTree.(IngressHostsTree)
if !ok {
return errors.Errorf("cache delete failed: invalid path tree value: got: %t", urlTree)
}

if err := ingressHostsTree.DeleteFunctionName(path, function); err != nil {
return errors.Wrap(err, "cache delete failed")
}

if ingressHostsTree.IsEmpty() {
// If the ingressHostsTree is empty after deletion, remove the host from the cache
ic.logger.DebugWith("cache delete: host removed as it is empty",
"host", host)
ic.syncMap.Delete(host)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ic.logger.DebugWith("cache delete: host removed as it is empty",
"host", host)
ic.syncMap.Delete(host)
ic.syncMap.Delete(host)
ic.logger.DebugWith("cache delete: host removed as it is empty",
"host", host)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here

}

return nil
}

func (ic *IngressCache) Get(host, path string) ([]string, error) {
urlTree, exists := ic.syncMap.Load(host)
if !exists {
return nil, errors.New("cache get failed: host does not exist")
}

ingressHostsTree, ok := urlTree.(IngressHostsTree)
if !ok {
return nil, errors.Errorf("cache get failed: invalid path tree value: got: %t", urlTree)
}

result, err := ingressHostsTree.GetFunctionName(path)
if err != nil {
return nil, errors.Wrap(err, "cache get failed")
}

return result, nil
}
Loading