Skip to content
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

[SERIALIZATION] ClusterBean Serialization #1710

Closed
garyparrot opened this issue May 9, 2023 · 13 comments · Fixed by #1795
Closed

[SERIALIZATION] ClusterBean Serialization #1710

garyparrot opened this issue May 9, 2023 · 13 comments · Fixed by #1795

Comments

@garyparrot
Copy link
Collaborator

未來 #1693 會有序列化/反序列化 ClusterBean 的需求。

這個 Issue 用來討論該如何實作這個需求。

目前最主要的問題是 ClusterBean 內部的變數 Map<Integer, Collection<HasBeanObject>>,這個變數中有用到 HasBeanObject,這個物件目前是一個 Interface,專案內有許多 HasBeanObject 是透過 lambda 而非 class 的方式來建立,由於 lambda 建立的 class 是匿名的,我們很難在別的 JVM 上重建這個匿名的 type。

@chia7712
Copy link
Contributor

chia7712 commented May 9, 2023

@garyparrot 我想確認一下,在 benchmark 中應該還是會用到 MetricStore吧?如果會用到的話,我們可以只保存 BeanObject,然後利用把BeanObject匯入 MetricStore 後使用 MetricSensor重建ClusterBean,你覺得如何?

@chinghongfang 可否也看一下?

@garyparrot
Copy link
Collaborator Author

@garyparrot 我想確認一下,在 benchmark 中應該還是會用到 MetricStore吧?如果會用到的話,我們可以只保存 BeanObject,然後利用把BeanObject匯入 MetricStore 後使用 MetricSensor重建ClusterBean,你覺得如何?

我幾個小時前有想到這種方案,我想概念上應該是可行的。如果走上這條路的話,我想我們的序列化檔案裡面可能是存類似 Map<Integer, Collection<BeanObject> 形式的資料。

@chia7712
Copy link
Contributor

chia7712 commented May 9, 2023

我想概念上應該是可行的。如果走上這條路的話,我想我們的序列化檔案裡面可能是存類似 Map<Integer, Collection 形式的資料。

感謝確認,直覺上的風險是 sensor 並不接受“過時“的 bean object ?

@garyparrot
Copy link
Collaborator Author

感謝確認,直覺上的風險是 sensor 並不接受“過時“的 bean object ?

感謝提醒,我忘了 MetricSensor 的介面有這種用途。他的介面裡面可以進行任何形式的過濾或產生新 Metrics,而非單純 BeanObject 到 HasBeanObject 的轉型。

不過,透過過濾產新 Metrics 的這個過程,這樣出來的成品好像已經不是 HasBeanObject (吐出來的物件不是某個 JVM MBean Server 來的 BeanObject),而是一個基於 BeanObject 和其他複雜邏輯加值出來的額外資訊

@chia7712
Copy link
Contributor

不過,透過過濾產新 Metrics 的這個過程,這樣出來的成品好像已經不是 HasBeanObject (吐出來的物件不是某個 JVM MBean Server 來的 BeanObject),而是一個基於 BeanObject 和其他複雜邏輯加值出來的額外資訊

沒錯,就是這點讓我擔心。嚴格上來說這是一段「運算過程」,而這個運算過程如果會跟「時間」有關的話,那麼過時的BeanObject就可能導致錯誤。

不過另一個角度是說,如果運算過程會在意資料過時,那麼可能連算成本的時候都會在意資料過時,那可能該cost function 就無法用來做benchmark了

我第一個想到可能會有這個問題的就是「流量成本」, @qoo332001 可否麻煩你也協助確認一下?

@garyparrot
Copy link
Collaborator Author

或是說回去嘗試序列化整個 Map<Integer, Collection<HasBeanObject>> 的方案,因為這個是 Balancer 最直覺的輸入,如果能夠從這個層面還原的話就沒有上述的這些問題,我嘗試用 LambdaMetafactory.metafactory 在 Runtime 動態建立特定 Interface 的 Lambda,測試可以把 HasBeanObject 原本的形態還原回來,下面的程式碼是這個概念的 Demo

import org.astraea.common.metrics.BeanObject;
import org.astraea.common.metrics.HasBeanObject;
import org.astraea.common.metrics.broker.HasGauge;
import org.astraea.common.metrics.connector.ConnectorInfo;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;

public class IgnoreInvokeDynamic {

  @Test
  @Disabled
  void testInvokeDynamic() throws Throwable {
    {
      // these two variables come from the deserialization result
      var deserializedBeanObject = new BeanObject("Domain", Map.of("Hello", "World"), Map.of());
      var targetInterface = HasGauge.class;
      run(deserializedBeanObject, targetInterface);
    }

    {
      // these two variables come from the deserialization result
      var deserializedBeanObject = new BeanObject("Domain", Map.of("Hello", "5566"), Map.of());
      var targetInterface = ConnectorInfo.class;
      run(deserializedBeanObject, targetInterface);
    }
  }

  void run(BeanObject beanObject, Class<?> typeInterface) throws Throwable {
    if(!typeInterface.isInterface())
      throw new IllegalArgumentException("The given type has to be interface");
    if(typeInterface.isAssignableFrom(HasBeanObject.class))
      throw new IllegalArgumentException("The target interface must be assignable from HasBeanObject");

    // dynamically create an object with that target type:
    // this will work as long as the target type is a functional interface and 
    // original object didn't store state somewhere else
    var callsite = LambdaMetafactory.metafactory(
        MethodHandles.lookup(),
        "beanObject",
        MethodType.methodType(typeInterface, BeanObject.class),
        MethodType.methodType(BeanObject.class),
        MethodHandles.lookup().findStatic(this.getClass(), "call", MethodType.methodType(BeanObject.class, BeanObject.class)),
        MethodType.methodType(BeanObject.class));
    var deserializedHasBeanObject = (HasBeanObject) callsite.getTarget().invoke(beanObject);
    System.out.println(deserializedHasBeanObject.beanObject());
    System.out.println(deserializedHasBeanObject.getClass());
    System.out.println(deserializedHasBeanObject.getClass().getInterfaces()[0].getName());
    System.out.println(deserializedHasBeanObject instanceof HasBeanObject);
    System.out.println(typeInterface.isAssignableFrom(deserializedHasBeanObject.getClass()));
    System.out.println();
  }

  static BeanObject call(BeanObject capture) {
    return capture;
  }

}
BeanObject[domainName=Domain, properties={Hello=World}, attributes={}, createdTimestamp=1683713096411]
class org.astraea.common.IgnoreInvokeDynamic$$Lambda$365/0x0000000800ca8880
org.astraea.common.metrics.broker.HasGauge
true
true

BeanObject[domainName=Domain, properties={Hello=5566}, attributes={}, createdTimestamp=1683713096432]
class org.astraea.common.IgnoreInvokeDynamic$$Lambda$366/0x0000000800ca8d30
org.astraea.common.metrics.connector.ConnectorInfo
true
true

@chia7712
Copy link
Contributor

或是說回去嘗試序列化整個 Map<Integer, Collection> 的方案

這個方案會變成「動態產生的物件」也需要被序列化保存,這個方案在相容性上會有些麻煩,出錯的時候也很難debug,我比較偏向只序列化「固定的物件」也就是 BeanObject,當然這個也有副作用就是前面提到如果有cost很在意當下時間的beans就會出問題...但目前應該沒有這種例子?

@garyparrot
Copy link
Collaborator Author

這個方案會變成「動態產生的物件」也需要被序列化保存,這個方案在相容性上會有些麻煩,出錯的時候也很難debug

同意這段,如果 Client 沒有對應的 Interface Class 會有恢復上的問題,之前測試的時候也發現這個做法 beanObject() 函數不能被 debugger step in。

當然這個也有副作用就是前面提到如果有cost很在意當下時間的beans就會出問題...但目前應該沒有這種例子?

剛剛檢查了一下,目前沒有這種例子

@chia7712
Copy link
Contributor

@garyparrot 如果你同意這個做法的話,麻煩說個 +1 :)

@garyparrot
Copy link
Collaborator Author

這個做法有他的疑慮,不過 +1 :3

@chia7712
Copy link
Contributor

@garyparrot 感謝回應

@Haser0305 @chaohengstudent 這部分的序列化設計可以麻煩你們實作嗎?

@Haser0305
Copy link
Collaborator

@Haser0305 @chaohengstudent 這部分的序列化設計可以麻煩你們實作嗎?

好的,沒問題

@garyparrot garyparrot assigned garyparrot and unassigned garyparrot May 22, 2023
@garyparrot
Copy link
Collaborator Author

#1506 重複

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants