Skip to content

feature: dynamically add and remove shards by the ring client #2093

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

Closed
wants to merge 1 commit into from

Conversation

szuecs
Copy link
Contributor

@szuecs szuecs commented May 25, 2022

fix #2077
feature: ring.SetAddrs to add and remove shards by the ring client #2093
tests: to scale in and out

Signed-off-by: Sandor Szücs sandor.szuecs@zalando.de

@szuecs szuecs changed the title feature: ring.SetAddrs to add and remove shards by the ring client feature: dynamically add and remove shards by the ring client May 25, 2022
szuecs added a commit to zalando/skipper that referenced this pull request May 25, 2022
In case of kubernetes dataclient we can detect the number of redis shards available and trigger an update to the redis ringclient.
In order to use this feature we have to use a forked version of go-redis until it is merged and released redis/go-redis#2093

Signed-off-by: Sandor Szücs <sandor.szuecs@zalando.de>
@vmihailenco
Copy link
Collaborator

The code could be cleaner if you restructure it like this:

shards := newRingShards(addrs, c.shards)
c.mu.Lock()
if c.closed {
    // close shards
} else {
    c.shards = shards
    c.list = shards.list()
}
c.mu.Unlock()

What do you think?

@szuecs
Copy link
Contributor Author

szuecs commented Jun 7, 2022

@vmihailenco so you are suggesting a type change of c.shards to be able to add a .list() ?
I think the problem will be that we would need to lock this .list() and here we don't need it.
We could also add an intermediate type, but then we would do:

shards := newRingShards(addrs, c.shards)
c.mu.Lock()
if c.closed {
    // close shards
} else {
    c.shards = shards.shards() // returns map[string]*ringShard
    c.list = shards.list()
}
c.mu.Unlock()

@vmihailenco
Copy link
Collaborator

@vmihailenco so you are suggesting a type change of c.shards to be able to add a .list() ?

I suggest to slightly refactor ringShards type so we can fully replace it in SetAddrs holding the lock:

// SetAddrs is safe to be used concurrently.
func (c *Ring) SetAddrs(addrs map[string]string) {
    // Construct new shards without holding the lock. Pass old shards to reuse existing clients.
    shards := newRingShards(addrs, c.shards)

    c.shardsMu.Lock()
    defer c.shardsMu.Unlock()

    if c.closed {
        // Ring is closed. Close new shards.
        shards.Close()
        return
    }

    // Atomically set new shards so they are used by the Ring.
    c.shards = shards
}

Does that make sense?

@szuecs
Copy link
Contributor Author

szuecs commented Jun 10, 2022

@vmihailenco yeah thanks, makes a lot of sense!

@AlexanderYastrebov
Copy link
Contributor

I suggest to slightly refactor ringShards type so we can fully replace it in SetAddrs holding the lock:

This does not look to be safe for concurrent use: imagine two threads call SetAddrs, each creates a new shards and then consequently they take a lock and update c.shards - the second thread would overwrite c.shards and thus leak shards created by the first thread.

@AlexanderYastrebov
Copy link
Contributor

As an idea we may have another mutex to guard only SetAddrs and Close (need to make sure to lock it first in both methods https://stackoverflow.com/questions/38489190/holding-two-mutex-locks-at-the-same-time).
This way we ensure no two threads execute SetAddrs/Close while address change is in progress. SetAddrs would only check c.closed once and thus will not need to cleanup newly created shards.

@vmihailenco
Copy link
Collaborator

As an idea we may have another mutex

Yes, that is what I suggest as well.

@szuecs
Copy link
Contributor Author

szuecs commented Jun 13, 2022

@AlexanderYastrebov great idea, I hope this is now good, but also happy to rewrite. :)

@szuecs
Copy link
Contributor Author

szuecs commented Jun 15, 2022

@vmihailenco if you give it another try for a review would be great. I had an online session with @AlexanderYastrebov and rewrote some parts to make it nicer.

In an unrelated follow up PR I would like to enhance the locking in ring.go and for example remove the current rebalance()

@szuecs szuecs force-pushed the master branch 2 times, most recently from 2f67804 to d1c1751 Compare July 20, 2022 20:55
szuecs added a commit to zalando/skipper that referenced this pull request Jul 21, 2022
In case of kubernetes dataclient we can detect the number of redis shards available and trigger an update to the redis ringclient.
In order to use this feature we have to use a forked version of go-redis until it is merged and released redis/go-redis#2093

Signed-off-by: Sandor Szücs <sandor.szuecs@zalando.de>
szuecs added a commit to zalando/skipper that referenced this pull request Jul 21, 2022
In case of kubernetes dataclient we can detect the number of redis shards available and trigger an update to the redis ringclient.
In order to use this feature we have to use a forked version of go-redis until it is merged and released redis/go-redis#2093

Signed-off-by: Sandor Szücs <sandor.szuecs@zalando.de>
szuecs added a commit to zalando/skipper that referenced this pull request Jul 26, 2022
* feature: dynamic detect and resize redis instances

In case of kubernetes dataclient we can detect the number of redis shards available and trigger an update to the redis ringclient.
In order to use this feature we have to use a forked version of go-redis until it is merged and released redis/go-redis#2093


Signed-off-by: Sandor Szücs <sandor.szuecs@zalando.de>
…euse old connections

test: ring scale-in and scale-out

rewrite as suggested by @AlexanderYastrebov
Signed-off-by: Sandor Szücs <sandor.szuecs@zalando.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add/remove shards in redis ring
3 participants