How to disable retry in Socket Mode?
akshayamaldhure opened this issue · comments
I'm trying to build a bot listener service in Java by listening to the AppMentionEvent
, but I see that whenever the task takes more than 3000 milliseconds, I receive the messages multiple times.
Reproducible in:
implementation("com.slack.api:bolt-socket-mode:1.36.1")
implementation("javax.websocket:javax.websocket-api:1.1")
implementation("org.glassfish.tyrus.bundles:tyrus-standalone-client:1.19")
implementation("org.slf4j:slf4j-simple:1.7.36")
The Slack SDK version
| +--- com.slack.api:slack-api-client:1.1.0 -> 1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1
+--- com.slack.api:bolt-socket-mode:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1 (*)
| | +--- com.slack.api:slack-api-client:1.36.1 (*)
| \--- com.slack.api:bolt:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1 (*)
+--- com.slack.api:bolt-socket-mode:1.36.1 (n)
| +--- com.slack.api:slack-api-client:1.1.0 -> 1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1
+--- com.slack.api:bolt-socket-mode:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1 (*)
| | +--- com.slack.api:slack-api-client:1.36.1 (*)
| \--- com.slack.api:bolt:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.1.0 -> 1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1
+--- com.slack.api:bolt-socket-mode:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1 (*)
| | +--- com.slack.api:slack-api-client:1.36.1 (*)
| \--- com.slack.api:bolt:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.1.0 -> 1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1
+--- com.slack.api:bolt-socket-mode:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1
| | +--- com.slack.api:slack-api-model:1.36.1 (*)
| | +--- com.slack.api:slack-api-client:1.36.1 (*)
| \--- com.slack.api:bolt:1.36.1
| +--- com.slack.api:slack-api-model:1.36.1 (*)
| +--- com.slack.api:slack-api-client:1.36.1 (*)
| +--- com.slack.api:slack-app-backend:1.36.1 (*)
Java Runtime version
openjdk version "11.0.17" 2022-10-18 LTS
OS info
ProductName: macOS
ProductVersion: 14.1.1
BuildVersion: 23B81
Darwin Kernel Version 23.1.0: Mon Oct 9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Steps to reproduce:
package slackBot;
import com.slack.api.bolt.App;
import com.slack.api.bolt.socket_mode.SocketModeApp;
import com.slack.api.methods.response.chat.ChatPostMessageResponse;
import com.slack.api.model.event.AppMentionEvent;
public class Main {
public static void main(String[] args) throws Exception {
// App expects an env variable: SLACK_BOT_TOKEN
App app = new App();
final String MESSAGE_TEXT_SEPARATOR = " ";
app.event(AppMentionEvent.class, (payload, context) -> {
AppMentionEvent event = payload.getEvent();
String eventMessageText = payload.getEvent().getText();
context.logger.info("Received message: " + eventMessageText);
ChatPostMessageResponse replyMessage;
String command = eventMessageText.split(MESSAGE_TEXT_SEPARATOR)[1];
switch (command) {
case "timeout":
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
replyMessage = context.client().chatPostMessage(r -> r.channel(event.getChannel()).threadTs(event.getTs()).text("We have timed out."));
break;
case "hi":
replyMessage = context.client().chatPostMessage(r -> r.channel(event.getChannel()).threadTs(event.getTs()).text("Hey there"));
break;
default:
replyMessage = context.client().chatPostMessage(r -> r.channel(event.getChannel()).threadTs(event.getTs()).text("I don't understand what you're saying."));
break;
}
if (replyMessage == null || !replyMessage.isOk()) {
assert replyMessage != null;
context.logger.error("chat.postMessage failed: " + replyMessage.getError());
} else {
context.logger.info("Responded to the " + command + " related message");
}
return context.ack();
});
// SocketModeApp expects an env variable: SLACK_APP_TOKEN
new SocketModeApp(app).start();
}
}
Expected result:
I'm looking for some way to avoid the automatic retries that the Slack SDK is doing.
Actual result:
I tried to do context.getAdditionalValues().put("X-Slack-No-Retry", "1");
to no avail. I always get multiple responses in case of the timeout
command I defined in my code. But that never happens for the other commands in the code.
Hi @akshayamaldhure, thanks for asking the question.
Instead of disabling the retries, we recomend using app.executorService().submit( ... )
to run time-consuming tasks in your listener. See #982 (comment) for more details.
If your app has an additional overhead outside the listener function (e.g., cold-start time when spinning up app instance), which can badly affect the response time, improving the runtime performance will be also necessary to make the app to stably function.
I hope this helps.
Hi @akshayamaldhure, thanks for asking the question.
Instead of disabling the retries, we recomend using
app.executorService().submit( ... )
to run time-consuming tasks in your listener. See #982 (comment) for more details.If your app has an additional overhead outside the listener function (e.g., cold-start time when spinning up app instance), which can badly affect the response time, improving the runtime performance will be also necessary to make the app to stably function.
I hope this helps.
Thanks @seratch for the quick response to my question. Could you please point me to a code example that uses app.executorService().submit( ... )
in Java? Basically, I'm trying to understand how app.executorService().submit( ... )
is supposed to work with AppMentionEvent
.
Here is a simple example demonstrating how to do so with app_mention events: https://github.com/slackapi/java-slack-sdk/blob/v1.36.1/bolt-servlet/src/test/java/samples/S3OAuthSample.java#L29-L38
Thanks a lot @seratch once again. Modifying my code like this helps.
package slackBot;
import com.slack.api.bolt.App;
import com.slack.api.bolt.socket_mode.SocketModeApp;
import com.slack.api.methods.response.chat.ChatPostMessageResponse;
import com.slack.api.model.event.AppMentionEvent;
public class Main {
public static void main(String[] args) throws Exception {
// App expects an env variable: SLACK_BOT_TOKEN
App app = new App();
final String MESSAGE_TEXT_SEPARATOR = " ";
app.event(AppMentionEvent.class, (payload, context) -> {
AppMentionEvent event = payload.getEvent();
String eventMessageText = payload.getEvent().getText();
context.logger.info("Received message: " + eventMessageText);
String command = eventMessageText.split(MESSAGE_TEXT_SEPARATOR)[1];
app.executorService().submit(() -> {
ChatPostMessageResponse replyMessage;
try {
switch (command) {
case "timeout":
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
replyMessage = context.client().chatPostMessage(r -> r.channel(event.getChannel()).threadTs(event.getTs()).text("We have timed out."));
break;
case "hi":
replyMessage = context.client().chatPostMessage(r -> r.channel(event.getChannel()).threadTs(event.getTs()).text("Hey there"));
break;
default:
replyMessage = context.client().chatPostMessage(r -> r.channel(event.getChannel()).threadTs(event.getTs()).text("I don't understand what you're saying."));
break;
}
if (replyMessage == null || !replyMessage.isOk()) {
assert replyMessage != null;
context.logger.error("chat.postMessage failed: " + replyMessage.getError());
} else {
context.logger.info("Responded to the " + command + " related message");
}
} catch (Exception e) {
context.logger.error("Something went wrong: " + e.getMessage());
}
});
return context.ack();
});
// SocketModeApp expects an env variable: SLACK_APP_TOKEN
new SocketModeApp(app).start();
}
}