-
Notifications
You must be signed in to change notification settings - Fork 56
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
Passthrough service hangs under high load #6474
Comments
The hanging happens in the client level and some how the service also hangs. This issue seems to be related to the changes done with the status code binding feature: ballerina-platform/module-ballerina-http#1915, specifically when the client call involves data-binding where we call a Ballerina function from Java to bind the payload to the required type: https://github.com/ballerina-platform/module-ballerina-http/blob/8e3226c5f8cc9cb0a5e2cb043ccc140644b5e7ec/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternResponseProcessor.java#L519 This should be an issue with the client's concurrent behaviour when data binding is involved. The client behaviour with this status code binding feature looks like this: Ballerina - Client call(External function) => Java - Client Execution method => Ballerina - Client call implementation function => Ballerina - Process response function(External function) => Java - Process response method => Java - Perform data-binding method => Ballerina - Perform data-binding method The last 3 steps are just to build the payload with the required type, and based on the target type, further processing may happen. Without these steps i.e. if we bind it to Reduced reproducer:
import ballerina/http;
import ballerina/io;
type Location record {|
string name;
string id;
string address;
|};
type Locations record {|
*http:Links;
Location[] locations;
|};
final http:Client snowpeakEP = check new ("http://localhost:9090");
public function main() returns error? {
future<error?> futureResult;
foreach int i in 0...400 {
futureResult = start test(i);
}
_ = check wait futureResult;
}
function test(int i) returns error? {
io:println("Request started for ", i);
Locations locations = check snowpeakEP->get("/snowpeak/locations");
io:println("Request completed for ", i);
}
import ballerina/http;
type Location record {|
string name;
string id;
string address;
|};
type Locations record {|
*http:Links;
Location[] locations;
|};
service /snowpeak on new http:Listener(9090) {
resource function get locations() returns Locations {
return {
locations: [
{name: "Snowpeak", id: "1", address: "1234 Snowpeak"},
{name: "Snowpeak", id: "2", address: "1234 Snowpeak"},
{name: "Snowpeak", id: "3", address: "1234 Snowpeak"}
],
_links: {
self: {href: "/snowpeak/locations"}
}
};
}
} I will further investigate on this |
Can we get a JAVA thread dump and a ballerina strand dump together when this happens? |
Following interop function async call inside the foreach loop hangs: import ballerina/io;
import ballerina/jballerina.java;
public function main() returns error? {
future<error?> futureResult;
foreach int i in 0...25 {
futureResult = start test(i);
// check test(i);
}
_ = check wait futureResult;
}
public class ABC {
public int port;
public isolated function init(int port) {
self.port = port;
}
public isolated function getPort() returns int {
return self.port;
}
}
function test(int i) returns error? {
io:println("Request started for ", i);
ABC abc = new ABC(9090);
io:println(getResource(abc));
io:println("Request completed for ", i);
}
isolated function getResource(ABC abc) returns anydata = @java:Method {
'class: "myapp.app.App"
} external;
public static Object getResource(Environment env, BObject client) {
final Object[] out = new Object[1];
CountDownLatch countDownLatch = new CountDownLatch(1);
Callback callback = new Callback() {
@Override
public void notifySuccess(Object o) {
out[0] = o;
countDownLatch.countDown();
}
@Override
public void notifyFailure(BError bError) {
out[0] = bError;
countDownLatch.countDown();
}
};
env.getRuntime().invokeMethodAsyncSequentially(client, "getPort", null,null, callback, null, PredefinedTypes.TYPE_INT);
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return out[0];
} Strand Dump:Ballerina Strand Dump [2024/04/19 16:18:41] =========================================== |
This is for the HTTP client: |
With this PR: ballerina-platform/module-ballerina-http#1954, we have used a different approach to avoid countdown latch and moved the new feature to a new client type. The root cause should be found from the lang side, so moving this to lang |
This is wrong. We cannot block the strand thread using countdown latch or any other concurrency mechanism. We need to use markasync and future.complete methods to block the strand without blocking underlying thread. Refer [1] for more info. |
The logic used here which uses countdown latch can be replaced with markasync and Ballerina future. The post processing logics can be moved to the notifySuccess() method of the callback. public static void getResource(Environment env, BObject obj) {
Future balFuture = env.markAsync();
Callback callback = getCallback(balFuture);
env.getRuntime().invokeMethodAsyncConcurrently(obj, "getPort", "interop-async-seq",null, callback, null, PredefinedTypes.TYPE_INT);
}
private static Callback getCallback(Future balFuture) {
final Object[] out = new Object[1];
return new Callback() {
@Override
public void notifySuccess(Object o) {
out[0] = ((Long) o) + 2;
balFuture.complete(out[0]);
}
@Override
public void notifyFailure(BError bError) {
balFuture.complete(new RuntimeException(bError.toString()));
}
}; So, transferred this issue to ballerina-lib. |
Ack
Ack. Since we have found a workaround in this PR: ballerina-platform/module-ballerina-http#1954, we will use this approach for the moment |
Closing this issue since the original implementation is not valid according to the Ballerina runtime implementation |
Description:
$Subject under load test.
Strand dump - https://gist.github.com/xlight05/af107fdd097dd6825fbe6bd6e13efa1e
Simpler sample -
Steps to reproduce:
docker run -p 9090:9090 ktharmi176/snowpeak:latest
<http_path>/target/ballerina-runtime/bin/bal run
curl http://localhost:9091/passthrough
wrk -t12 -c400 -d10s http://localhost:9091/passthrough
curl http://localhost:9091/passthrough
Service should be hanging at this point.
Affected Versions:
2201.9.0-20240418-164300-a37919c4
OS, DB, other environment details and versions:
Related Issues (optional):
Suggested Labels (optional):
Suggested Assignees (optional):
The text was updated successfully, but these errors were encountered: