Rx Cache - Replay operator Clear - c#

I am using the following code from here - looks like an issue to me in clearing the "Replay cache"
https://gist.github.com/leeoades/4115023
If I change the following call and code like this I see that there is bug in Replay i.e. it is never cleared. Can someone please help to rectify this ?
private Cache<string> GetCalculator()
{
var calculation = Observable.Create<string>(o =>
{
_calculationStartedCount++;
return Observable.Timer(_calculationDuration, _testScheduler)
.Select(_ => "Hello World!" + _calculationStartedCount) // suffixed the string with count to test the behaviour of Replay clearing
.Subscribe(o);
});
return new Cache<string>(calculation);
}
[Test]
public void After_Calling_GetResult_Calling_ClearResult_and_GetResult_should_perform_calculation_again()
{
// ARRANGE
var calculator = GetCalculator();
calculator.GetValue().Subscribe();
_testScheduler.Start();
// ACT
calculator.Clear();
string result = null;
calculator.GetValue().Subscribe(r => result = r);
_testScheduler.Start();
// ASSERT
Assert.That(_calculationStartedCount, Is.EqualTo(2));
Assert.That(result, Is.EqualTo("Hello World!2")); // always returns Hello World!1 and not Hello World!2
Assert.IsNotNull(result);
}

The problem is a subtle one. The source sequence Timer completes after it emits an event, which in turn calls OnCompleted on the internal ReplaySubject created by Replay. When a Subject completes it no longer accepts any new values even if a new Observable shows up.
When you resubscribe to the underlying Observable it executes again, but isn't able to restart the Subject, so your new Observer can only receive the most recent value before the ReplaySubject completed.
The simplest solution would probably just be to never let the source stream complete (untested):
public Cache(IObservable<T> source)
{
//Not sure why you are wrapping this in an Observable.create
_source = source.Concat(Observable.Never())
.Replay(1, Scheduler.Immediate);
}

Related

Using BufferBlock as Observable without consuming the elements

We use BufferBlocks to build a small simulation tool where we want to find areas that takes a long time to complete. Producers and Consumers of the blocks will essentially sleep for x amount of time and then post a message to another block.
We decided to use an Observer pattern. Howver, I see some behavior I did not expect. Whenever the OnNext method of the observers is called the BufferBlock is empty (Count == 0). This is problematic as I want only 1 observer to be able to fetch the value from the queue.
Is there a way to change this behavior? If not, how should I handle consumption from the BufferBlocks?
Currently I want to be able to do something similar to post the messages and have all Observers try to fetch it:
public void OnNext(Object value)
{
var res =this.AsConsumer().ConsumeQueue.ReceiveAsync().Result;
Thread.Sleep(this.TimeToConsume );
ProduceQueue.Post(someOtherValue);
}
I have written some tests to show the behavior of the BufferBlock.
[Test]
public void
WhenObservingMocks_CallsOnNextForAllMocks()
{
var firstObserver = new Mock<IObserver<int>>();
var secondObserver = new Mock<IObserver<int>>();
var block = new BufferBlock<int>();
block.AsObservable().Subscribe(firstObserver.Object);
block.AsObservable().Subscribe(secondObserver.Object);
block.Post(2);
Thread.Sleep(TimeSpan.FromMilliseconds(50));
firstObserver.Verify(e => e.OnNext(It.IsAny<int>()), Times.Once);
secondObserver.Verify(e => e.OnNext(It.IsAny<int>()), Times.Once);
}
[Test]
public void
WhenHavingObservers_DoesConsumesTheElementFromQueue()
{
var firstObserver = new Mock<IObserver<int>>();
var secondObserver = new Mock<IObserver<int>>();
var block = new BufferBlock<int>();
block.AsObservable().Subscribe(firstObserver.Object);
block.AsObservable().Subscribe(secondObserver.Object);
block.Post(2);
Assert.Zero(block.Count);
}
[Test]
public void
WhenPostingOnce_CanOnlyReceiveOnce()
{
var block = new BufferBlock<int>();
block.Post(2);
Assert.True(block.TryReceive(out int _));
Assert.False(block.TryReceive(out int _));
}

Testing an IConnectableObservable with the TestScheduler

Ok, it's late but I can't for the life of me work out why the following is happening.
I am trying to test the following (simplified) IConnectableObservable<long>:
private const int PollingIntervalMinutes = 5;
private IConnectableObservable<long> CreateObservable(IScheduler scheduler)
{
return Observable
.Interval(TimeSpan.FromMinutes(PollingIntervalMinutes), scheduler)
.StartWith(0)
.Publish();
}
If I test it "long hand" as follows the test passes:
[Test]
public void ShouldReturnExpectedNumberOfMessagesLongHand()
{
var scheduler = new TestScheduler();
var observed = scheduler.CreateObserver<long>();
var observable = CreateObservable(scheduler);
observable.Subscribe(observed);
observable.Connect();
Assert.That(observed.Messages.Count, Is.EqualTo(1));
scheduler.AdvanceBy(TimeSpan.FromMinutes(PollingIntervalMinutes).Ticks);
Assert.That(observed.Messages.Count, Is.EqualTo(2));
scheduler.AdvanceBy(TimeSpan.FromMinutes(PollingIntervalMinutes).Ticks);
Assert.That(observed.Messages.Count, Is.EqualTo(3));
scheduler.AdvanceBy(TimeSpan.FromMinutes(PollingIntervalMinutes).Ticks);
Assert.That(observed.Messages.Count, Is.EqualTo(4));
}
However, if I use the TestScheduler.Start approach - as follows - the test hangs and never reaches the Assert:
[Test]
public void ShouldReturnExpectedNumberOfMessages()
{
var scheduler = new TestScheduler();
var observable = CreateObservable(scheduler);
var observed = scheduler.Start(() => { observable.Connect(); return observable; }, TimeSpan.FromMinutes(PollingIntervalMinutes * 3).Ticks);
Assert.That(observed.Messages.Count, Is.EqualTo(4));
}
By placing a breakpoint in the observable (i.e. on an additional Select or Do) I can see that the call to scheduler.Start is causing the underlying observable to spin (i.e. hits the breakpoint thousands of times) instead of respecting the scheduled times.
I've tried various different means of calling Connect on the IConnectableObservable (i.e. connecting prior to calling start, scheduling a call to Connect in the TestScheduler, etc) but to no avail.
It is definitely related to testing an IConnectableObservable as removing the Publish (i.e. making it a normal cold observable) makes the test pass.
A sanity check and/or suggestions would be greatly appreciated.
The Undisposed Publisher strikes again.
The usual suspects:
var observable = CreateObservable(scheduler);
scheduler.Start(() => { observable.Connect(); return observable; }, ...
To actually dispose of the interval timer, you need a way to dispose the subscription from observable.Connect(), and not the subscription by the Start method.
Once you connect, your interval is cranking out items (as fast as it can) using the test scheduler, and the unsubscribe doesn't actually do anything, leaving it running - and the test scheduler will never complete.
One way of ensuring the disposal of resources, in general, is to use Using.
scheduler.Start(() => Observable.Using(() => observable.Connect(), _ => observable), ...
But a simpler way of ensuring that the original connection to publish is disposed when the downstream observable is unsubscribed from, is to use RefCount.
scheduler.Start(() => CreateObservable(scheduler).RefCount(), ...

Get first IObservable event without blocking the thread/task that wants it

I am looking at using IObservable to get a response in a request-response environment within a c# async methods and replace some older callback based code, but I am finding that if a value is pushed (Subject.OnNext) to the observable but FirstAsync is not yet at the await, then the FirstAsync is never given that message.
Is there a straightforward way to make it work, without a 2nd task/thread plus synchronisation?
public async Task<ResponseMessage> Send(RequestMessage message)
{
var id = Guid.NewGuid();
var ret = Inbound.FirstAsync((x) => x.id == id).Timeout(timeout); // Never even gets invoked if response is too fast
await DoSendMessage(id, message);
return await ret; // Will sometimes miss the event/message
}
// somewhere else reading the socket in a loop
// may or may not be the thread calling Send
Inbound = subject.AsObservable();
while (cond)
{
...
subject.OnNext(message);
}
I cant put the await for the FirstAsync simply before I send the request, as that would prevent the request being sent.
The await will subscribe to the observable. You can separate the subscription from the await by calling ToTask:
public async Task<ResponseMessage> Send(RequestMessage message)
{
var id = Guid.NewGuid();
var ret = Inbound.FirstAsync((x) => x.id == id).Timeout(timeout).ToTask();
await DoSendMessage(id, message);
return await ret;
}
I took a closer look and there is a very easy solution to your problem by just converting hot into cold observable. Replace Subject with ReplaySubject. Here is the article: http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html.
Here is the explanation:
The Replay extension method allows you take an existing observable
sequence and give it 'replay' semantics as per ReplaySubject. As a
reminder, the ReplaySubject will cache all values so that any late
subscribers will also get all of the values.

Howto enforce object construction on first subscription only?

Taking my first steps with Rx I am stuck here:
public class DisposableResourceDemo : IDisposable
{
public DisposableResourceDemo() {
Console.WriteLine("DisposableResourceDemo constructor.");
}
public void Dispose() {
Console.WriteLine("DisposableResourceDemo.Dispose()");
}
public void SideEffect() {
Console.WriteLine("DisposableResourceDemo.SideEffect()");
}
}
[Test]
public void ShowBehaviourOfRxUsing()
{
var test = Observable.Using(() =>
{
// This should happen exactly once, independent of number of subscriptions,
// object should be disposed on last subscription disposal or OnCompleted call
return new DisposableResourceDemo();
},
(dr) =>
{
return Observable.Create<string>(
(IObserver<string> observer) =>
{
dr.SideEffect();
var dummySource = Observable.Return<string>("Some Text");
return dummySource.Subscribe(observer);
});
}).Publish().RefCount();
Console.WriteLine("before 1st subscription.");
test.Subscribe(Console.WriteLine, () => Console.WriteLine("OnCompleted in 1st."));
Console.WriteLine("before 2nd subscription.");
test.Subscribe(Console.WriteLine, () => Console.WriteLine("OnCompleted in 2nd."));
}
To my surprise the code above yields
before 1st subscription.
DisposableResourceDemo constructor.
DisposableResourceDemo.SideEffect()
Some Text
OnCompleted in 1st.
DisposableResourceDemo.Dispose()
before 2nd subscription.
--> [happy with missing "Some Text" here]
OnCompleted in 2nd.
--> [unhappy with second instantiation here]
DisposableResourceDemo constructor.
DisposableResourceDemo.SideEffect()
DisposableResourceDemo.Dispose()
Please note that calling Connect() manually after both subscriptions is not what I want here, though then the output is as expected.
I am not totally sure what you are trying to achieve here. It seems that you want to share the observable sequence and its related resources. So the standard ways to do this is with the ConnectableObservable types that you get from .Replay() and .Publish() etc
You say you dont want to use .Connect() and instead you use .RefCount() which is very common. However, your sequence completes. You also are using the Extension method Subscribe(...) which will internally create an Auto detaching observer, i.e. when the sequence completes, it will disconnect.
So my question is, should the internal sequence actually complete?
If the answer is yes, then why would the 2nd subscription get the OnComplete notification...it has happened already, it is in the past. Maybe you do want to replay the OnComplete, in which case maybe .Replay(1) is what you want.
If the answer is no, then you can easily fix this by putting a Concat(Observable.Never<string>()) either before the .Publish() or after the Observable.Return.

Understand the flow of control when calling a blocking code from non-blocking block?

I have the following code
static void Main(string[] args)
{
//var source = BlockingMethod();
var source2 = NonBlocking();
source2.Subscribe(Console.WriteLine);
//source.Subscribe(Console.WriteLine);
Console.ReadLine();
}
private static IObservable<string> BlockingMethod()
{
var subject = new ReplaySubject<string>();
subject.OnNext("a");
subject.OnNext("b");
subject.OnCompleted();
Thread.Sleep(1000);
return subject;
}
private static IObservable<string> NonBlocking()
{
return Observable.Create<string>(
observable =>
{
observable.OnNext("c");
observable.OnNext("d");
observable.OnCompleted();
//Thread.Sleep(1000);
var source = BlockingMethod();
source.Subscribe(Console.WriteLine);
return Disposable.Create(() => Console.WriteLine("Observer has unsubscribed"));
//or can return an Action like
//return () => Console.WriteLine("Observer has unsubscribed");
});
}
}
which prints
c
d
Observer has unsubscribed
a
b
Can anyone help me get the flow of the control in the program. I did try reading the Call Stack etc..but could not understand everything.
EDIT
Why do i get the above output(which i assume is right) instead of
c
d
a
b
Observer has unsubscribed
The difference in your expected behaviour and the actual behaviour comes from the following line:
var subject = new ReplaySubject<string>();
By default a ReplaySubject uses the Scheduler.CurrentThread. It's as if you declared it like so:
var subject = new ReplaySubject<string>(Scheduler.CurrentThread);
When scheduling using the current thread you get your actions queued up - waiting for the currently executing code to complete before it starts. If you want the code to run immediately you need to use Scheduler.Immediate like so:
var subject = new ReplaySubject<string>(Scheduler.Immediate);
Does this explain it sufficiently?

Categories