Skip to content
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

ResourceWatcher reconnection fixes #523

Merged
merged 4 commits into from
Feb 6, 2023
Merged

ResourceWatcher reconnection fixes #523

merged 4 commits into from
Feb 6, 2023

Conversation

tomasfabian
Copy link
Contributor

@tomasfabian tomasfabian commented Feb 4, 2023

Removed disposal of _reconnectHandler in ResourceWatcher which causes ObjectDisposedException after leader reelections and the watcher stops observing the resource. #522

The situation happens after a consecutive call to StartAsync after StopAsync:

await _watcher.StartAsync();

await _watcher.StopAsync();

await _watcher.StartAsync();

The _reconnectHandler should be disposed only in the if statement of the Disposing method:

private void Disposing(bool fromStop)
{
        if (!fromStop)
        {
            _watchEvents.Dispose();
            _reconnectHandler.Dispose();
        }

        // _reconnectHandler.Dispose(); // this causes an error later on
        //...
}

Thx

@tomasfabian
Copy link
Contributor Author

tomasfabian commented Feb 4, 2023

I think that the subscription from the ctor should be also moved to StartAsync since it is disposed in StopAsync:

        _reconnectSubscription =
            _reconnectHandler
                .Select(Observable.Timer)
                .Switch()
                .Retry()
                .Subscribe(async _ => await WatchResource(), error => _logger.LogError(error, $"There was an error while restarting the resource watcher {typeof(TEntity)}"));

The reconnect subscription should be either recreated on each StartAsync or not disposed during CloseAsync in case it should be active during the whole lifetime of the object.

Let me know please which one is the preferred solution and I can do it, too. Thx.

EDIT: For now I moved the disposal of _reconnectSubscription into the if statement (will not be executed for CloseAsync).

@tomasfabian tomasfabian changed the title removed disposal of reconnect handler in ResourceWatcher which causes… ResourceWatcher reconnection fixes Feb 4, 2023
@buehler
Copy link
Owner

buehler commented Feb 6, 2023

Hey @tomasfabian
I guess not dispose it during closing is fine. It is not that big of a memory overhead and as long as one solution (or rather "fix") is in place we should be fine.

Would you mind to add a test case for this scenario? It should be fairly easy to add a test where you call the sequence of methods as mentioned in the issue and this PR and see if the await method throws an error.

Thank you for the contribution!

@tomasfabian
Copy link
Contributor Author

Hello @buehler,
ResourceWatcher<TEntity> is not prepared for unit testing. I'm not able to easily mock the ResourceWatcherMetrics<TEntity> ATM. It would require to wrap also the Prometheus dependencies such as Gauge.Child, because they have constructors with an internal modifier etc.

What do you think / suggest?

@tomasfabian
Copy link
Contributor Author

tomasfabian commented Feb 6, 2023

I can introduce a new interface IResourceWatcherMetrics<TEntity> would you accept it as part of this PR, please?

public interface IResourceWatcherMetrics<TEntity> : IResourceWatcherMetrics
    where TEntity : IKubernetesObject<V1ObjectMeta>
{
}

public interface IResourceWatcherMetrics
{
    IGauge Running { get; }
    // Gauge.Child Running { get; }

    ICounter WatchedEvents { get; }
    // Counter.Child WatchedEvents { get; }

    ICounter WatcherExceptions { get; }
    // Counter.Child WatcherExceptions { get; }

    ICounter WatcherClosed { get; }
    // Counter.Child WatcherClosed { get; }
}

internal class ResourceWatcherMetrics<TEntity> : IResourceWatcherMetrics<TEntity>
    where TEntity : IKubernetesObject<V1ObjectMeta>
{
  // ...
}

And then in the OperatorBuilder I have to change the DI registration:

Services.AddSingleton(typeof(IResourceWatcherMetrics<>), typeof(ResourceWatcherMetrics<>));
// Services.AddSingleton(typeof(ResourceWatcherMetrics<>));

Thx

@buehler
Copy link
Owner

buehler commented Feb 6, 2023

Ahh I see, yeah, you're right. No, then we'll leave it this way. If there is another issue with this matter, we can introduce an internal interface for this and add tests.

@buehler buehler merged commit 79646b3 into buehler:master Feb 6, 2023
buehler pushed a commit that referenced this pull request Feb 10, 2023
… tests (#526)

This PR enables testability for the `ResourceWatcher<TEntity>` class see
also #523 by extracting a new interface
`IResourceWatcherMetrics<TEntity>`.
buehler added a commit that referenced this pull request Feb 14, 2023
#482 #522 #523 #526

---------

Co-authored-by: Christoph Bühler <buehler@users.noreply.github.com>
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.

None yet

2 participants