gravitino icon indicating copy to clipboard operation
gravitino copied to clipboard

[#6486] feat(iceberg): supports change Iceberg request in pre event listener

Open FANNG1 opened this issue 10 months ago • 17 comments

What changes were proposed in this pull request?

Use an wrapper class to allow user create an new request, and the dispatcher operator use the new created request. The event listener could leverage ObjectWrapper to create new Iceberg request.

  public void onPreEvent(PreEvent preEvent) {
    if (preEvent instanceof IcebergCreateTablePreEvent) {
      ObjectWrapper<CreateTableRequest> objectWrapper = ((IcebergCreateTablePreEvent) preEvent).createTableRequestWrapper();
      CreateTableRequest originalRequest = objectWrapper.get();
      // create newRequest according to your business logic
      objectWrapper.set(newRequest);
    }
  }

Why are the changes needed?

Fix: #6486

Does this PR introduce any user-facing change?

no

How was this patch tested?

change the iceberg request and it take affects when creating table in local enviroment

FANNG1 avatar Feb 19 '25 10:02 FANNG1

@jerryshao @puchengy PTAL.

FANNG1 avatar Feb 19 '25 10:02 FANNG1

@FANNG1 LGTM, can we also support Update table as well to begin with? Thanks

puchengy avatar Feb 19 '25 14:02 puchengy

I have a concern with this piece solution. Either we need to augment the create table request to meet the requirement, or we need a global solution for events. By global solution, I mean we may need a wrapper pattern for all events. Of course we can get his one case handled today, but in the long run, we have to make sure the same thing won't happen again.

tengqm avatar Feb 20 '25 00:02 tengqm

@puchengy @tengqm thanks for your advise, use a more general solution by objectWrapper to wrap the Iceberg REST request in pre event, please help to review again.

FANNG1 avatar Feb 20 '25 01:02 FANNG1

Regarding this PR, I think it is clean and okay to go ahead. Wondering if we need an umbrella issue for handling other events so that we won't get the ball dropped.

tengqm avatar Feb 20 '25 03:02 tengqm

On a quick review, my feeling is that using a wrapper is not so intuitive, do we have any better solution to handle such scenario?

jerryshao avatar Feb 21 '25 11:02 jerryshao

On a quick review, my feeling is that using a wrapper is not so intuitive, do we have any better solution to handle such scenario?

One thing I am thinking is if we can create a MutableRequestBuilder that wraps the exiting Request, the existing Iceberg request builder does not support get method (maybe something good to contribute) otherwise below is my proposal:

class MutableCreateTableRequestBuilder {
        // clone existing unmodifiable CreateTableRequest into a modifiable builder
        MutableCreateTableRequestBuilder(CreateTableRequest request) {
              this.name = request.name()
              this.properties = request.properties()
              ...
        }
       
       void setProperties(Map properties) {}
       Map getProperties() {}

       void setLocation(String newLocation) {}
       String getLocation() {}

       ...

       CreateTableRequest build() {
            CreateTableRequest.Builder builder = ...
            builder.set...
            builcer.set...
            return builder.build()
       }
}

As a result MutableCreateTableRequestBuilder will be the element that is passed around and IRC will be responsible to call build() function

@FANNG1 thought?

puchengy avatar Feb 21 '25 14:02 puchengy

On a quick review, my feeling is that using a wrapper is not so intuitive, do we have any better solution to handle such scenario?

One thing I am thinking is if we can create a MutableRequestBuilder that wraps the exiting Request, the existing Iceberg request builder does not support get method (maybe something good to contribute) otherwise below is my proposal:

class MutableCreateTableRequestBuilder {
        // clone existing unmodifiable CreateTableRequest into a modifiable builder
        MutableCreateTableRequestBuilder(CreateTableRequest request) {
              this.name = request.name()
              this.properties = request.properties()
              ...
        }
       
       void setProperties(Map properties) {}
       Map getProperties() {}

       void setLocation(String newLocation) {}
       String getLocation() {}

       ...

       CreateTableRequest build() {
            CreateTableRequest.Builder builder = ...
            builder.set...
            builcer.set...
            return builder.build()
       }
}

As a result MutableCreateTableRequestBuilder will be the element that is passed around and IRC will be responsible to call build() function

@FANNG1 thought?

I'm not sure whether MutableCreateTableRequestBuilder is suitable to place in Gravitino Iceberg REST server code, as it's tied to Iceberg request implemetation.

FANNG1 avatar Feb 24 '25 02:02 FANNG1

On a quick review, my feeling is that using a wrapper is not so intuitive, do we have any better solution to handle such scenario?

Let me take more time to think about it

FANNG1 avatar Feb 24 '25 02:02 FANNG1

@jerryshao Do we need include this PR in 0.9 version?

jerqi avatar Apr 10 '25 07:04 jerqi

How about defining a transform interface like T -> T for pre-event? If we have to introduce this layer, we'd better make it more clear for the users, The concept of wrapper seems too hacky.

jerryshao avatar Apr 14 '25 04:04 jerryshao

@jerryshao that SGTM

puchengy avatar Apr 15 '25 03:04 puchengy

@jerryshao @puchengy , I add a new method in EventListenerPlugin to transform the pre-event, is it the right direction? If yes, I'll do more work to polish the implementation. thx.

  /**
   * Transform a preEvent before it is processed by the listener.
   *
   * <p>This method allows the plugin to modify the preEvent before it is processed. The sequence
   * of the transformations is determined by the event listener configuration order.
   *
   * @param preEvent The preEvent to be transformed.
   * @return The transformed preEvent.
   */
  default PreEvent transformPreEvent(PreEvent preEvent) {
    return preEvent;
  };

FANNG1 avatar Apr 23 '25 08:04 FANNG1

@FANNG1 Hi thanks for working on this.

The sequence of the transformations is determined by the event listener configuration order.

Can you share a little more on how it will look like? Thanks

puchengy avatar Apr 23 '25 16:04 puchengy

@jennywang67 fyi

puchengy avatar Apr 23 '25 16:04 puchengy

@FANNG1 Hi thanks for working on this.

The sequence of the transformations is determined by the event listener configuration order.

Can you share a little more on how it will look like? Thanks

We may add multiple event listeners like gravitino.eventListener.names = listener1, listener2, the transform logic in listener1 is executed first. But after another thought I prefer doesn't give such guarantee for transform order, because the transform logic should not have dependences, we should grant the transform logic is invoked before process logic in onPreEvent. WDYT?

FANNG1 avatar Apr 24 '25 01:04 FANNG1

@FANNG1 SGTM, I don't have much preference right, maybe I will be able to comment on once you have a PR

puchengy avatar Apr 24 '25 04:04 puchengy

@jerryshao, do you think this is the right direction?

FANNG1 avatar Jun 09 '25 03:06 FANNG1

Adding SupportsChangingPreEvent interface to declare whether a pre-event supports changing the content to avoid the scenario that the event listener changes the content of the pre-event, but it doesn't take effect.

FANNG1 avatar Jun 13 '25 03:06 FANNG1

I'm generally OK with this solution, but I think the PR is not ready, right? We shall support SupportsChangingPreEvent for preevent, right?

jerryshao avatar Jul 01 '25 02:07 jerryshao

I'm generally OK with this solution, but I think the PR is not ready, right? We shall support SupportsChangingPreEvent for preevent, right?

Yes, this is the draft stage to show the solutions. I'll continue the work if no opposing comments. cc @puchengy

FANNG1 avatar Jul 01 '25 04:07 FANNG1

seems no requirement to changing load/list/delete/exists requests, so only make create/update pre-event could be modified, though implementing SupportsChangingPreEvent. @jerryshao @puchengy PTAL.

FANNG1 avatar Jul 01 '25 11:07 FANNG1

@FANNG1 Thanks for the work. I will spend sometime tomorrow to look at the PR!

puchengy avatar Jul 02 '25 05:07 puchengy

@puchengy are there any other comments?

FANNG1 avatar Jul 08 '25 06:07 FANNG1

@FANNG1 Taking a look.

puchengy avatar Jul 08 '25 16:07 puchengy

@FANNG1 it looks good to me. Thanks

puchengy avatar Jul 08 '25 17:07 puchengy