moby / moby/swarmkit

[Proposal] Package watch and manager/state Watch functions clean up

Open
#2,629 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
3.7k
Forks
676
Avg merge
4d 9h
Merged PRs (30d)
6

Description

Problem description

When reading the manager/state/store package's code I realized there were different Watch methods and functions in different packages that were tangling the code:

  • watch.Queue type has 4 related methods (watch/watch.go):
    1. *Queue.Watch() (chan events.Event, func())
    2. *Queue.WatchContext(conterxt.Context) (chan events.Event)
    3. *Queue.CallbackWatch(events.Matcher) (chan events.Event, func())
    4. *Queue.CallbackWatchContext(conterxt.Context, events.Event) (chan events.Event)
  • state package has a function (manager/state/watch.go):
    1. Watch(*watch.Queue, ...api.Event) (chan events.Event, func())
  • store.MemoryStore type has a related method (manager/state/store/memory.go):
    1. *MemoryStore.WatchQueue() *watch.Queue
  • store package also has 2 functions (manager/state/store/memory.go):
    1. ViewAndWatch(*MemoryStore, func(ReadTx) error, ...api.Event) (chan events.Event, func(), error)
    2. ViewFrom(*MemoryStore, *api.Version, ...api.Event) (chan events.Event, func(), error)

The hetereogenity of functions and methods, using context or cancel functions, etc are tangling the code, which means that the appropiate function is not being used.

watch.Queue

The second and forth methods (the ones using a context.Context instead of giving a cancel function) are not being used except for a single test that can be implemented in a simpler way with the cancel function ones.

The third one (CallbackWatch) is not actually offering a callback as the name suggests. It is actually providing a filter on the events that can be received. FilteredWatch or just Watch (swapping the first to WatchAll`) fit the function better.

state.Watch

This method translates a variadic number of api.Events into an event.Matcher and applies it to the provided *watch.Queue's CallbackWatch method.
Not providing this functionality in the base watch.Queue makes sense as its not something generic, it's specific to the state package, and more specifically to the store.MemoryStore type's queue.
However, providing this functionality as a store.MemoryStore method makes even more sense.

store.MemoryStore

The WatchQueue method name is redundant as it is always followed by one of the watch.Queue watch methods. Furthermore, if the previous change is applied, the access to the queue for the method could be direct. In that case this method will not be needed anymore. This change has a hidden advantage, not returning the Queue would restrict Publishing on it to the MemoryStore which is the only one that should be able to do it.

store.ViewAndWatch

This method uses *MemoryStore.Update just to lock the queue and ensure that the View callback and the Watch channel are in sync, and no event is lost nor sended when it shouldn't. If it is transformed into a MemoryStore method it could access the lock and thus use MemoryStore.View instead.
Additionally it would give this function/method more visibility, as there are several places in the code where it should be used and instead a separate call to Watch and View are being done, thus not completely ensuring the previous conditions.

The View callback also differs in signature, as this method returns an error, making it equal to the Update callback but with different privileges.

store.WatchFrom

Similar to the previous, this method also lacks visibility and uses MemoryStore.Update when it shouldn't.

Proposal

  • Delete watch.Queue's context related methods, simplifying the only remaining use in a test case.
  • Rename watch.Queue's remaining methods to improve redability.
  • Transform state.Watch, store.ViewAndWatch and store.WatchFrom into store.MemoryStore's methods.
  • Make ViewAndWatch callback the same than MemoryStore.View.
  • Update the cases where two different calls are being made instead of using ViewAndWatch.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading watch/watch.go, manager/state/watch.go, and manager/state/store/memory.go, then search for all listed Watch, ViewAndWatch, WatchFrom, and WatchQueue call sites. Done means the proposed methods are consolidated or renamed, callers use the updated store methods, and the existing test suite passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
distributed-systems
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.