-
Notifications
You must be signed in to change notification settings - Fork 7
🌱 Force Cache Refresh when Server was not found by name. #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
3742073
a8f2d65
98d256a
1d9d7da
2997209
750b722
6078c4d
b890440
5197fb8
6029b1f
a2dfd76
ca4fd5d
945a2ea
f032308
88585c9
4cfba46
57538d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package client | ||
|
|
||
| import ( | ||
| hrobot "github.com/syself/hrobot-go" | ||
| "github.com/syself/hrobot-go/models" | ||
| ) | ||
|
|
||
| var _ Client = &adapter{} | ||
|
|
||
| type adapter struct { | ||
| hrobot.RobotClient | ||
| } | ||
|
|
||
| // New wraps a plain Robot client so it satisfies the local Client interface. | ||
| func New(robotClient hrobot.RobotClient) Client { | ||
| if robotClient == nil { | ||
| return nil | ||
| } | ||
| return &adapter{RobotClient: robotClient} | ||
| } | ||
|
|
||
| // ServerGetListForceRefresh falls back to the plain list call because the | ||
| // uncached behavior only exists in the cache-backed client implementation. | ||
| func (a *adapter) ServerGetListForceRefresh(_ string) ([]models.Server, error) { | ||
| return a.ServerGetList() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ const ( | |
| robotUserNameENVVar = "ROBOT_USER_NAME" | ||
| robotPasswordENVVar = "ROBOT_PASSWORD" | ||
| cacheTimeoutENVVar = "CACHE_TIMEOUT" | ||
| defaultCacheTimeout = 5 * time.Minute | ||
| ) | ||
|
|
||
| var _ robotclient.Client = &cacheRobotClient{} | ||
|
|
@@ -27,10 +28,16 @@ type cacheRobotClient struct { | |
| timeout time.Duration | ||
|
|
||
| lastUpdate time.Time | ||
| now func() time.Time | ||
|
|
||
| // cache | ||
| l []models.Server | ||
| m map[int]*models.Server | ||
|
|
||
| // forcedRefreshServerNames stores when a node name last triggered a forced Robot server list | ||
| // refresh. While that timestamp is still within the cache timeout window, repeated lookups for | ||
| // the same missing server name skip the extra uncached Robot API call. | ||
| forcedRefreshServerNames map[string]time.Time | ||
| } | ||
|
|
||
| // NewCachedRobotClient creates a new robot client with caching enabled. | ||
|
|
@@ -51,7 +58,7 @@ func NewCachedRobotClient(rootDir string, httpClient *http.Client, baseURL strin | |
| } | ||
|
|
||
| if cacheTimeout == 0 { | ||
| cacheTimeout = 5 * time.Minute | ||
| cacheTimeout = defaultCacheTimeout | ||
| } | ||
|
|
||
| credentialsDir := credentials.GetDirectory(rootDir) | ||
|
|
@@ -85,6 +92,7 @@ func NewCachedRobotClient(rootDir string, httpClient *http.Client, baseURL strin | |
| handler := &cacheRobotClient{} | ||
| handler.timeout = cacheTimeout | ||
| handler.robotClient = c | ||
| handler.now = time.Now | ||
| return handler, nil | ||
| } | ||
|
|
||
|
|
@@ -105,7 +113,7 @@ func (c *cacheRobotClient) ServerGet(id int) (*models.Server, error) { | |
| } | ||
|
|
||
| // set time of last update | ||
| c.lastUpdate = time.Now() | ||
| c.lastUpdate = c.currentTime() | ||
| } | ||
|
|
||
| server, found := c.m[id] | ||
|
|
@@ -134,30 +142,91 @@ func (c *cacheRobotClient) ServerGetList() ([]models.Server, error) { | |
| } | ||
|
|
||
| // set time of last update | ||
| c.lastUpdate = time.Now() | ||
| c.lastUpdate = c.currentTime() | ||
| } | ||
|
|
||
| return c.l, nil | ||
| } | ||
|
|
||
| // ServerGetListForceRefresh invalidates the current cache and reloads the list | ||
| // from Robot unless nodeName already triggered a forced refresh within the | ||
| // current timeout window. | ||
| func (c *cacheRobotClient) ServerGetListForceRefresh(nodeName string) ([]models.Server, error) { | ||
| if nodeName != "" && c.nodeHasAlreadyForcedRefresh(nodeName) { | ||
| return c.ServerGetList() | ||
| } | ||
|
|
||
| c.m = nil | ||
janiskemper marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a comment that you do this in order to delete the cache and to call the actual API?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. part of 57538d0 |
||
| list, err := c.ServerGetList() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if nodeName != "" { | ||
guettli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| c.nodeTriggeredForcedRefresh(nodeName) | ||
| } | ||
| return list, nil | ||
| } | ||
|
|
||
| // nodeHasAlreadyForcedRefresh reports whether nodeName already triggered a | ||
| // forced refresh within the current cache timeout window. | ||
| func (c *cacheRobotClient) nodeHasAlreadyForcedRefresh(nodeName string) bool { | ||
| if c.forcedRefreshServerNames == nil { | ||
| return false | ||
| } | ||
|
|
||
| forcedAt, found := c.forcedRefreshServerNames[nodeName] | ||
guettli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if !found { | ||
| return false | ||
| } | ||
| if c.currentTime().After(forcedAt.Add(c.forceRefreshTimeout())) { | ||
| delete(c.forcedRefreshServerNames, nodeName) | ||
| return false | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| // nodeTriggeredForcedRefresh records that nodeName already triggered a forced | ||
| // refresh. | ||
| func (c *cacheRobotClient) nodeTriggeredForcedRefresh(nodeName string) { | ||
|
||
| if c.forcedRefreshServerNames == nil { | ||
| c.forcedRefreshServerNames = make(map[string]time.Time) | ||
| } | ||
| c.forcedRefreshServerNames[nodeName] = c.currentTime() | ||
| } | ||
|
|
||
| func (c *cacheRobotClient) shouldSync() bool { | ||
| // map is nil means we have no cached value yet | ||
| if c.m == nil { | ||
| c.m = make(map[int]*models.Server) | ||
| return true | ||
| } | ||
| if time.Now().After(c.lastUpdate.Add(c.timeout)) { | ||
| if c.currentTime().After(c.lastUpdate.Add(c.timeout)) { | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| func (c *cacheRobotClient) currentTime() time.Time { | ||
| if c.now == nil { | ||
| return time.Now() | ||
| } | ||
| return c.now() | ||
| } | ||
|
|
||
| func (c *cacheRobotClient) forceRefreshTimeout() time.Duration { | ||
| if c.timeout == 0 { | ||
| return defaultCacheTimeout | ||
| } | ||
| return c.timeout | ||
| } | ||
|
|
||
| func (c *cacheRobotClient) SetCredentials(username, password string) error { | ||
| err := c.robotClient.SetCredentials(username, password) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // The credentials have been updated, so we need to invalidate the cache. | ||
| c.m = nil | ||
| c.forcedRefreshServerNames = nil | ||
| return nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this comment:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you just check twice the same thing. How does that fit to the description? I still don't understand it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@janiskemper I updated the test and added coments: 4cfba46