Unexpected behavior of Rx.Observable.reduce takeUntil concat - system.reactive

I am surprised about the behavior of the following code (see https://plnkr.co/edit/OVc26DmXpvXqSOJsQAoh?p=preview):
let empty = Observable.empty();
let source = Observable.range(1, 5)
.map(i =>
Observable.timer(i * 2000, 1000).map(x => "source " + i + ": " + x).take(10))
.reduce((s1, s2) => s1.takeUntil(s2).concat(s2), empty)
.mergeAll();
var subscription = source.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
yields
Next: source 1: 0
Next: source 1: 1
--- a long pause here ---
Next: source 5: 0
Next: source 5: 1
Next: source 5: 2
Next: source 5: 3
Next: source 5: 4
Next: source 5: 5
Next: source 5: 6
Next: source 5: 7
Next: source 5: 8
Next: source 5: 9
Completed
but I had hoped to see all sequences show up inbetween. What went wrong?
Edit:
Note that using share()not always cures it. This code fails:
let originalSequence = Observable.timer(0, 1000).take(10).share();
let empty = Observable.empty();
let source = Observable.range(1, 5)
.map(i =>
originalSequence.delay(i * 2000).map(x => "source " + i + ": " + x))
.reduce((s1, s2) => s1.takeUntil(s2).concat(s2), empty)
.mergeAll();
and this code works as I expect and I do not understand why
let empty = Observable.empty();
let source = Observable.range(1, 5)
.map(i =>
Observable.timer(i * 2000, 1000).map(x => "source " + i + ": " + x).take(10).share())
.reduce((s1, s2) => s1.takeUntil(s2).concat(s2), empty)
.mergeAll();
Edit 2:
The C# version also has a behavior I would not expect, but at the same time behaves differently:
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
namespace RxScanProblem
{
class Program
{
static void Main(string[] args)
{
var originalSequence = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)).Take(10).Select(i => (long)i).Publish();
var empty = Observable.Empty<string>();
var source = Observable.Range(1, 5)
.Select(i => originalSequence.Delay(TimeSpan.FromSeconds(2 * i)).Select(x => "source " + i + ": " + x))
.Aggregate(empty, (s1, s2) => s1.TakeUntil(s2).Concat(s2))
.SelectMany(x => x);
source.Subscribe(
s => Console.WriteLine("Next: " + s),
ex => Console.WriteLine("Error: " + ex.Message),
() => Console.WriteLine("Completed"));
originalSequence.Connect();
// Dirty, I know
Thread.Sleep(20000);
}
}
}
yields (with some delay)
Next: source 1: 0
Next: source 1: 1
Next: source 1: 2
Edit 3
Also switch() does not behave as i would expect!
let empty = Observable.empty();
let source = Observable.range(1, 5)
.map(i => Observable.timer(i * 2000, 1000).map(x => "source " + i + ": " + x).take(10))
.switch();
yields
Next: source 5: 0
Next: source 5: 1
Next: source 5: 2
Next: source 5: 3
Next: source 5: 4
Next: source 5: 5
Next: source 5: 6
Next: source 5: 7
Next: source 5: 8
Next: source 5: 9
Same (!) behavior for C#
var originalSequence = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)).Take(10).Select(i => (long)i).Publish();
var empty = Observable.Empty<string>();
var source = Observable.Range(1, 5)
.Select(i => originalSequence.Delay(TimeSpan.FromSeconds(2 * i)).Select(x => "source " + i + ": " + x))
.Switch();

Lets look at what your code does.
let source = Observable.range(1, 5)
.map(i =>
Observable.timer(i * 2000, 1000).map(x => "source " + i + ": " + x).take(10))
.reduce((s1, s2) => s1.takeUntil(s2).concat(s2), empty)
.mergeAll();
First map will transform {1,2,3,4,5} into
s1 = Observable.timer(1 * 2000, 1000).map(x => "source 1: " + x).take(10));
s2 = Observable.timer(2 * 2000, 1000).map(x => "source 2: " + x).take(10));
s3 = Observable.timer(3 * 2000, 1000).map(x => "source 3: " + x).take(10));
s4 = Observable.timer(4 * 2000, 1000).map(x => "source 4: " + x).take(10));
s5 = Observable.timer(5 * 2000, 1000).map(x => "source 5: " + x).take(10));
next, reduce will glue them together like this:
s1.takeUntil(s2).concat(s2)
.takeUntil(s3).concat(s3)
.takeUntil(s4).concat(s4)
.takeUntil(s5).concat(s5)
now lets write a small marble to show what all these streams will yield:
s1 --0123456789
s2 ----0123456789
s3 ------0123456789
s4 --------0123456789
s5 ----------0123456789
s1.takeUntil(s2) --01|
.concat(s2) --01----0123456789
takeUntil(s3) --01--|
.concat(s3) --01--------0123456789
takeUntil(s4) --01----|
.concat(s4) --01------------0123456789
takeUntil(s5) --01------|
.concat(s5) --01----------------0123456789
Now, if you use share(), you effectively publish the source. Publish means you multicast to all subscribers at the same time. This works well if there are 2 subscribers, even if one arrives later than the other, the source will continue mid-stream for the second subscriber. Things change when the first subscriber disconnect before the second one arrive. To preserve resources, share() will disconnect the source, and resubscribe to it later. Given you started with cold observables, this means they will start again at the beginning, with the long wait.
Since you use .takeUntil(s2).concat(s2), you will actually unsubscribe from s2 right before you subscribe to s2 again. After all concat wont connect until it receives completed from takeUntil and takeUntil wont emit completed until s2 has yielded. If s2 yields, takeUntil will instantly unsubscribe from it before forwarding completed down stream. This means that s2 will have no subscribers for a split second, and the source will be reset.
What you probably expected was that s2 would stay connected the entire time, and would keep running in the background. This would work if you had used a hot observable resulting from an active source instead of a cold observable made hot trough share().
I will not go into detail on switch() since i believe you already understood the problem there: It will disconnect the previous source when the next one arrives, not when the next one yields.
What you can do, is write your own 'switchOnYield'
source.publish(src => src
.flatMap(inner1 =>
inner1.takeUntil(src.flatMap(inner2 => inner2.take(1)))
))
What this does is merge all sources from source together, but add takeUntil on them with all latter sources. If any of the later sources yields, the first bunch will be unsubscribed. This works because the first time src yields, .flatMap(inner1 will run. The second time it yields, src.flatMap(inner2 will merge any item from the later source into the takeUntil operator.
demo here

I would strongly suggest you post expected or desired output. It's not clear what you want. For the C# sample, changing source to the following gets you closer (I think, again, I'm not sure):
var source = Observable.Range(1, 5)
.Select(i => originalSequence
.Delay(TimeSpan.FromSeconds(2 * i))
.Select(x => "source " + i + ": " + x)
)
//making sure s2 is shared properly, thus concated properly
.Aggregate(empty, (s1, s2) => s2.Publish( _s2 => s1
.TakeUntil(_s2)
.Concat(_s2)
))
.SelectMany(x => x);
That yields the following output:
Next: source 1: 0
Next: source 1: 1
Next: source 2: 1
Next: source 3: 1
Next: source 4: 1
Next: source 5: 1
Next: source 5: 2
Next: source 5: 3
Next: source 5: 4
Next: source 5: 5
Next: source 5: 6
Next: source 5: 7
Next: source 5: 8
Next: source 5: 9
Completed
This makes sense to me. If you can post desired output, I'll help you get there.

Related

How to pass correlated value in next request in Gatling script

How can I replace "details1" in "request_2" with correlated value "SynchToken" from "request_1". I am trying to replace with ${SynchToken} but it is not reflecting the correlated value.
val Transaction_Name_1 = group("Transaction_Name_1") {
exec(http("request_1")
.get(session => "/abc/details1?_=" + System.currentTimeMillis())
.check(regex("""name="SYNCHRONIZER_TOKEN" value="(.*?)"""").saveAs("SynchToken")))
.pause(5)
.exec(http("request_2")
.get(session => "/abc/details1?_=" + System.currentTimeMillis()))
}
You should really spend some time reading the documentation.
Here, you need to use the Session API.
exec(http("request_2")
.get(session => "/abc/" + session("SynchToken").as[String] + "?_=" + System.currentTimeMillis()))

Getting Object tag value in AWS S3

I am using scala to get information about my objects that are in S3 I am intersted in getting each object I have inside S3 Bucket his tag value so far I have accomplished this code to get me information about my objects but did not succeeded in getting its tagging value: I used this code in scala:
def retrieveObjectTags(keyName: String): Unit ={
try {
println("Listing objects")
val req: ListObjectsV2Request =
new ListObjectsV2Request().withBucketName(bucketName).withMaxKeys(2)
var result: ListObjectsV2Result = null
do {
result = client.listObjectsV2(req)
for (objectSummary <- result.getObjectSummaries) {
println(
" - " + objectSummary.getKey + " " + "(size = " + objectSummary.getSize +
")")
println(objectSummary.getETag)
}
println("Next Continuation Token : " + result.getNextContinuationToken)
req.setContinuationToken(result.getNextContinuationToken)
} while (result.isTruncated == true);
}catch {
case ase: AmazonServiceException => {
println(
"Caught an AmazonServiceException, " + "which means your request made it " +
"to Amazon S3, but was rejected with an error response " +
"for some reason.")
println("Error Message: " + ase.getMessage)
println("HTTP Status Code: " + ase.getStatusCode)
println("AWS Error Code: " + ase.getErrorCode)
println("Error Type: " + ase.getErrorType)
println("Request ID: " + ase.getRequestId)
}
case ace: AmazonClientException => {
println(
"Caught an AmazonClientException, " + "which means the client encountered " +
"an internal error while trying to communicate" +
" with S3, " +
"such as not being able to access the network.")
println("Error Message: " + ace.getMessage)
}
}
// val getTaggingRequest = new GetObjectTaggingRequest(bucketName,keyName)
// var getTagResult = client.getObjectTagging(getTaggingRequest)
//println(getTaggingRequest)
var tag: Tag = new Tag()
println("tag name:" + tag.getValue)
}
as for the remarked lines I have encounter a problem with it, what other way I can use to solve this problem?

cannot capture NumberFormatException in a unit test

i have a unit test which have to fail at purpose, but I cannot capture it, so it is weird.
This is how it looks the csv file:
curva;clase;divisa;rw
AED_FXDEP;OIS;AED;240,1000
ARS :Std;6m;ARS;240
AUD_CALMNY_DISC;OIS;AUD;169.7056275
AUD_DEPO_BBSW;6m;AUD;169.7056275
AUD_DEPO_BBSW;6m;AUD;
And this is the content of the json schema file:
{"type" : "struct","fields" : [ {"name" : "curve","type" : "string","nullable" : false}, {"name":"class", "type":"string", "nullable":false}, {"name":"currency", "type":"string", "nullable":false}, {"name":"rw", "type":"string","nullable":false} ]
I think it is self explainable, the last line of the csv has an empty field and that is not permitted, the exception is clear, NumberFormatException because you can create a number with an empty value. I want to catch the exception in the unit test, why I can't reach it?
This is the code that provokes the exception:
try{
val validateGenericFile : Boolean = CSVtoParquet.validateGenericCSV(pathCSVWithHeaderWithErrors,
pathCurvasJsonSchemaWithDecimal,
_nullValue,
_delimiter,
sc,
sqlContext)
//never reach!
Assert.assertTrue(validateGenericFile)
} catch {
case e:NumberFormatException => Assert.assertTrue("ERROR! " + e.getLocalizedMessage,false)
case ex:Exception => Assert.assertTrue("ERROR! " + ex.getLocalizedMessage,false)
} finally {
println("Done testValidateInputFilesFRTBSTDES436_WithErrors!")
}
the method validateGenericCSV looks like:
val myDfWithCustomSchema = _sqlContext.read.format("com.databricks.spark.csv").
option("header", "true").
option("delimiter", _delimiter).
option("nullValue", _nullValue).
option("mode","FAILFAST").
schema(mySchemaStructType).
load(fileToReview)
var finallyCorrect : Boolean = true
var contLinesProcessed = 1
try{
//this line provokes the exception!
val myArray = myDfWithCustomSchema.collect
var contElementosJson = 0
var isTheLineCorrect: Boolean = true
myArray.foreach { elem =>
println("Processing line with content: " + elem)
for (myElem <- myList) {
val actualType = myElem.`type`
val actualName = myElem.name
val actualNullable = myElem.nullable
if (contElementosJson == myList.size) {
contElementosJson = 0
}
if (actualType == "string") {
val theField = elem.getString(contElementosJson)
val validatingField: Boolean = theField.isInstanceOf[String]
isTheLineCorrect = validatingField && !((theField == "" || theField == null) && !actualNullable)
contElementosJson += 1
if (!isTheLineCorrect){
finallyCorrect=false
println("ATTENTION! an empty string chain. " + "Check this field " + actualName + " in the csv file, which should be a " + actualType + " according with the json schema file, can be nullable? " + actualNullable + " isTheLineCorrect? " + isTheLineCorrect)
}
} else if (actualType == "integer") {
val theField = elem.get(contElementosJson)
val validatingField: Boolean = theField.isInstanceOf[Integer]
isTheLineCorrect = validatingField && !((theField == "" || theField == null) && !actualNullable)
contElementosJson += 1
if (!isTheLineCorrect){
finallyCorrect=false
println("ATTENTION! an empty string chain. " + "Check this field " + actualName + " in the csv file, which should be a " + actualType + " according with the json schema file, can be nullable? " + actualNullable + " isTheLineCorrect? " + isTheLineCorrect)
}
} else if (actualType.startsWith("decimal")) {
val theField = elem.get(contElementosJson)
val validatingField: Boolean = theField.isInstanceOf[java.math.BigDecimal]
isTheLineCorrect = validatingField && !((theField == "" || theField == null) && !actualNullable)
contElementosJson += 1
if (!isTheLineCorrect){
finallyCorrect=false
println("ATTENTION! an empty string chain. " + "Check this field " + actualName + " in the csv file, which should be a " + actualType + " according with the json schema file, can be nullable? " + actualNullable + " isTheLineCorrect? " + isTheLineCorrect)
}
} else {
println("Attention! se está intentando procesar una columna del tipo " + actualType + " que no está prevista procesar. Comprobar.")
}
} //for
contLinesProcessed += 1
} //foreach))
} catch {
//NEVER REACHED! why????
case e:NumberFormatException => throw e
case ex:Exception => throw ex
}
Why the NumberFormatException is never reached within in validateGenericCSV method?
UPDATE
i have modified these lines:
case e:NumberFormatException => Assert.assertTrue("ERROR! " + e.getLocalizedMessage,true)
case ex:Exception => Assert.assertTrue("ERROR! " + ex.getLocalizedMessage,true)
for these lines:
case e:NumberFormatException => Assert.assertTrue("ERROR! " + e.getLocalizedMessage,false)
case ex:Exception => Assert.assertTrue("ERROR! " + ex.getLocalizedMessage,false)
The same error, my problem is that I cannot reach to the catch sentences when the exception happens!
Thank you
When inspecting the stack trace, we can see the following:
ERROR! Job aborted due to stage failure: Task 1 in stage 1.0 failed 1 times, most recent failure: Lost task 1.0 in stage 1.0 (TID 2, localhost): java.lang.NumberFormatException
Spark is a distributed computing framework. The NumberFormatException is taking place remotely at one of the executors while processing a task. Spark gets a TaskFailure from that executor and propagates the exception wrapped in a org.apache.spark.SparkException to the action that triggered the materialization of the computation: the .collect() method in this specific case.
If we would like to get the reason behind the failure, we can use ex.getCause.
In practical terms we will have something like this snippet:
catch {
case ex:Exception if ex.getCause.getClass == classOf[NumberFormatException] => Assert.fail("Number parsing failed" + e.getLocalizedMessage)
case ex:Exception => Assert.fail(...)
}
The test won't fail because Assert.assertTrue(...,true) does not fail. assertTrue fails if the second parameter is false but not when it's true.

Gatling: Web Socket Open and Initialization Index page is giving an Error

When I'm initializing(POST INDEX PAGE) my index page, it is giving me the following error.
KO bodyString.find.transform.exists failed, could not extract: transform crashed: Unexpected character ('2' (code 50)): was expecting comma to separate OBJECT entries
Code:
.exec(http("POST INDEX PAGE")
.post("/?v-1471231389581")
// .headers(Map("Content-Type" -> "application/json; charset=UTF-8"))
.formParam("v-browserDetails","1")
.formParam("theme", "mytheme")
.formParam("v-appId", appver)
.formParam("v-sh", "1200")
.formParam("v-sw", "1920")
.formParam("v-cw", "147")
.formParam("v-ch", "1047")
.formParam("v-curdate", "1470999686031")
.formParam("v-tzo", "-330")
.formParam("v-dstd", "0")
.formParam("v-rtzo", "-330")
.formParam("v-dston", "false")
.formParam("v-vw", "147")
.formParam("v-vh", "0")
.formParam("v-loc", baseurl + "/")
.formParam("v-wn", appver + "-0.7179318188297512")
.check(Checker.httpChecker)
).pause(1 seconds)
Checker:
val httpChecker = bodyString.transform {
(resp, session) =>
val state = new VaadinState;
println("\n resp :"+resp+"\n")
println("\n session :"+session+"\n")
println("Started user " + session.get("userName").as[String] + " " + session.get("password").as[String]);
state.userName = session.get("userName").as[String];
HttpRequestCreator.userStates += (session.get("userName").as[String] -> state);
//Find and store jsessionId
val url = new URL( new AppConfig().getBaseURL() );
val jsessionCookie = session("gatling.http.cookies").as[CookieJar].get(Uri.create( url.getProtocol+"://" + url.getHost+url.getPath+"/")).find(_.getName == "JSESSIONID");
state.jsessionid = jsessionCookie.getOrElse(null).getValue;
println("\n jsession id"+state.jsessionid+"\n")
println("\n resp :"+resp+ "\n")
state.readJsonState(state.httpResponseToValidJsonString(resp));
}
After that I've tried without posting those parameters. Then the sync id of the response is giving -1 even though it says web socket is open.
10:41:22.143 [DEBUG] i.g.h.a.w.WsActor - Received text message on websocket 'gatling.http.webSocket':237|for(;;);[{"changes":{},"resources":{},"locales":{},"meta":{"appError":{"caption":"Communication problem","url":null,"message":"Take note of any unsaved data, and <u>click here</u> or press ESC to continue.","details":null}},"syncId":-1}

Why makes calling error or done in a BodyParser's Iteratee the request hang in Play Framework 2.0?

I am trying to understand the reactive I/O concepts of Play 2.0 framework. In order to get a better understanding from the start I decided to skip the framework's helpers to construct iteratees of different kinds and to write a custom Iteratee from scratch to be used by a BodyParser to parse a request body.
Starting with the information available in Iteratees and ScalaBodyParser docs and two presentations about play reactive I/O this is what I came up with:
import play.api.mvc._
import play.api.mvc.Results._
import play.api.libs.iteratee.{Iteratee, Input}
import play.api.libs.concurrent.Promise
import play.api.libs.iteratee.Input.{El, EOF, Empty}
01 object Upload extends Controller {
02 def send = Action(BodyParser(rh => new SomeIteratee)) { request =>
03 Ok("Done")
04 }
05 }
06
07 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Either[Result, Int]] {
08 println(state + " " + input + " " + received)
09
10 def fold[B](
11 done: (Either[Result, Int], Input[Array[Byte]]) => Promise[B],
12 cont: (Input[Array[Byte]] => Iteratee[Array[Byte], Either[Result, Int]]) => Promise[B],
13 error: (String, Input[Array[Byte]]) => Promise[B]
14 ): Promise[B] = state match {
15 case 'Done => { println("Done"); done(Right(received), Input.Empty) }
16 case 'Cont => cont(in => in match {
17 case in: El[Array[Byte]] => copy(input = in, received = received + in.e.length)
18 case Empty => copy(input = in)
19 case EOF => copy(state = 'Done, input = in)
20 case _ => copy(state = 'Error, input = in)
21 })
22 case _ => { println("Error"); error("Some error.", input) }
23 }
24 }
(Remark: All these things are new to me, so please forgive if something about this is total crap.)
The Iteratee is pretty dumb, it just reads all chunks, sums up the number of received bytes and prints out some messages. Everything works as expected when I call the controller action with some data - I can observe all chunks are received by the Iteratee and when all data is read it switches to state done and the request ends.
Now I started to play around with the code because I wanted to see the behaviour for these two cases:
Switching into state error before all input is read.
Switching into state done before all input is read and returning a Result instead of the Int.
My understanding of the documentation mentioned above is that both should be possible but actually I am not able to understand the observed behaviour. To test the first case I changed line 17 of the above code to:
17 case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)
So I just added a condition to switch into the error state if more than 10000 bytes were received. The output I get is this:
'Cont Empty 0
'Cont El([B#38ecece6) 8192
'Error El([B#4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Then the request hangs forever and never ends. My expectation from the above mentioned docs was that when I call the error function inside fold of an Iteratee the processing should be stopped. What is happening here is that the Iteratee's fold method is called several times after error has been called - well and then the request hangs.
When I switch into done state before reading all input the behaviour is quite similar. Changing line 15 to:
15 case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }
and line 17 to:
17 case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length)
produces the following output:
'Cont Empty 0
'Cont El([B#16ce00a8) 8192
'Done El([B#2e8d214a) 16384
Done with El([B#2e8d214a)
Done with El([B#2e8d214a)
Done with El([B#2e8d214a)
Done with El([B#2e8d214a)
and again the request hangs forever.
My main question is why the request is hanging in the above mentioned cases. If anybody could shed light on this I would greatly appreciate it!
Your understanding is perfectly right and I have just push a fix to master:
https://github.com/playframework/Play20/commit/ef70e641d9114ff8225332bf18b4dd995bd39bcc
Fixed both cases plus exceptions in the Iteratees.
Nice use of copy in case class for doing an Iteratee BTW.
Things must have changed with Play 2.1 - Promise is no longer parametric, and this example no longer compiles.