vert.x icon indicating copy to clipboard operation
vert.x copied to clipboard

ConcurrentModificationException in JsonObject.encode

Open glassfox opened this issue 2 years ago • 6 comments

ConcurrentModificationException has been occurred in JsonObject.encode. Following the inputs:

  1. Json Object is a part of Message from EventBus.publish event
  2. JsonCodec provided by JacksonFactory as singletone in spi manner
  3. Vertx version: 4.3.1
  4. Hard to reproduce
  5. Stack trace:
Unhandled exception
io.vertx.core.json.EncodeException: Failed to encode as JSON: (was java.util.ConcurrentModificationException) (through reference chain: java.util.LinkedHashMap["codes"]->java.util.LinkedHashMap["oRluxwlHiHIdcRUvmuJLeAD"]->java.util.LinkedHashMap["number"])
    at 
io.vertx.core.json.JsonObject.encode(JsonObject.java:728)

glassfox avatar Jun 14 '22 08:06 glassfox

which precise version of Vert.x

vietj avatar Jun 14 '22 08:06 vietj

can you show the exact code also that leads to that, without it we can only speculate.

vietj avatar Jun 14 '22 08:06 vietj

Version: vertx-4.3.1

Following code of JacksonFactory:

public class JacksonMysqlFactory extends JacksonFactory {
	public static final JacksonFactory INSTANCE = new JacksonFactory();

	public static final JacksonCodec CODEC;

	static {
		JacksonCodec codec;
		try {
			codec = new MysqlDatabindCodec();
		} catch (Throwable ignore) {
			ignore.printStackTrace();
			// No databind
			codec = new JacksonCodec();
		}
		CODEC = codec;
	}

	@Override
	public JsonCodec codec() {
		return CODEC;
	}
}

Currently I build JsonObject, suitable for reproducer.

glassfox avatar Jun 14 '22 08:06 glassfox

I cannot exactly reproduce the concurrent modification but I do see some odd behavior here:

public class MainVerticle extends AbstractVerticle {

  public static void main(String[] args) {
    Vertx.clusteredVertx(new VertxOptions())
      .compose(vertx -> {
        return vertx.deployVerticle(new MainVerticle());
      })
      .onFailure(err -> {
      err.printStackTrace();
      System.exit(1);
    });
  }

  @Override
  public void start() {

    vertx.eventBus()
      .<JsonObject>consumer("webrtc.test")
      .handler(msg -> {
        vertx.setTimer(1L, t -> {
          System.out.println(msg.body());
        });
      });

    final JsonObject json = new JsonObject().put("key1", "value1");

    vertx.eventBus().send("webrtc.test", json);
    json.remove("key1");
  }
}

When using a cluster eventbus, because the delivery is lazy, the delivered message will be empty:

{}

While if we're doing single vert.x:

public static void main(String[] args) {
  Vertx.vertx().deployVerticle(new MainVerticle())
    .onFailure(err -> {
    err.printStackTrace();
    System.exit(1);
  });
}

The message is copied earlier:

{"key1", "value1"}

pmlopes avatar Jun 14 '22 10:06 pmlopes

You will see that io.vertx.core.eventbus.impl.HandlerRegistration#deliverMessageLocally() copies the message at different times. This is called from sendOrPub()

pmlopes avatar Jun 14 '22 10:06 pmlopes

I agree that the body should not be lazily copied but instead when the message is sent ? so the sender can modify it after sending it.

vietj avatar Jun 15 '22 07:06 vietj