2018年5月21日 星期一

透過 Spring Data JPA 存取 DynamoDB

因為 Spring Data JPA 用來存取資料庫時很方便,然後 DynamoDB 又是屬於特別囉唆的東西,所以之前在研究 Spring Boot 時,就在想不知道 Spring 是否有支援 DynamoDB 的實作。很幸運地,社群真的有人做了 Spring Data JPA + DynamoDB [1-2] 造福大家!XD

2018年5月17日 星期四

在 GCP 上為 VM 設定防火牆

在雲端環境中,防火牆的概念其實跟傳統模式有點不同。傳統的防火牆概念是在作業系統上設定像是 iptables 等。但在雲端上,因為網路服務是由獨立的元件提供的,所以其實應該用 SdN(Software defined Network)的方式來思考這個問題。具體來說就是,網路相關的設定並不會在 VM 上設定,而是在 VM 外部設定,就像是真的有個牆放在 VM 前面這樣。

設定防火牆規則

在 GCP 中,網路服務的提供是由 VPC 來進行的,因此在設定時要先在「VPC networks」裡的「Firewall rules」新增防火牆規則。防火牆規則的內容中,最重要的在於這個規則要套用到哪些 VM,而決定套用對象的方式在於設定「Targets」。

Targets 有三種模式可以設定:

  1. All instances in the network
  2. Specified target tags
  3. Specified service account

如果想要簡單的話,可以設定成 1,也就是規則將套用到網路中所有的 VM。不過如果只想針對特定一些 VM 的話,用 2 比較合適。用 2 的模式時,會需要輸入對象的標籤名稱,假設我想要打開 22 port,所以我命名標籤叫做「allow-ssh」,代表只要 VM 有「allow-ssh」這個標籤,就要套用這條防火牆規則。

設定 VM 標籤

接下來是比較奇特的地方,在 VM 上要標剛剛寫的標籤。從「Compute Engine」進入「VM Instances」,然後編輯想要設定防火牆的 VM。編輯時,要注意要找的是「Network tags」(中文介面會寫「網路標籤」)而不是「Lables」(中文介面會寫「標籤」)……。因為剛剛在防火牆規則設定的標籤是「allow-ssh」,因此 VM 的 Network tags 裡也要設定相同的名字。設定完以後,儲存然後稍微等一下下,防火牆規則就會套上 VM 了。

2018年5月15日 星期二

Ethereum 基礎概念與原理

Ethereum 是一種分散式帳本的技術,雖然說大家更熟知的是「以太幣」這個虛擬貨幣~。不過這裡會嘗試從系統工程師的角度來看帶這個技術。

這篇文章大概會寫得有點雜亂,因為這主要是要紀錄一些本來我自己不太清楚的問題。如果需要比較完整、有條理的說明,推薦可以認真看看 [1]。

2018年5月7日 星期一

AWS DynamoDB SDK v2 基礎使用(四):讀取資料

在 DynamoDB 中,讀取被分成 Query 和 Scan 兩種行為
兩種行為的差異,主要來自於存取的範圍是否侷限於同一個 Partition。
這其實在之前(好久以前了呀….XD)的文章中有提到過,概念上可以當成一個 Partition 就是一台主機
在寫入資料時,作為 Partition Key 的值會決定資料會被存放在哪一台主機。
而讀取資料時,使用 Query 是在特定一台主機上讀取資料,而使用 Scan 則會在所有主機上讀取資料。

2018年4月24日 星期二

Logback 的 Log 篩選設定

平常比較容易遇到的有幾種案例:

  1. 我想要 logback 輸出所有的 log,但特定某些 package 想要隱藏 DEBUG 和 TRACE
  2. 我想要 logback 只輸出 INFO 以上的 log,但特定某些 package 想要輸出所有 log
  3. 我想要 logback 只輸出 INFO 以上的 log,但特定某些 package 想要輸出所有 log,而且某個 package 的 INFO 以上 log 要輸出到別的 appender

之前開發時,大多用到的是第一種,但偶而想要第二和第三種時,花了不少時間才調整出來
最後發現一開始對設定的認知其實有點不太正確。

2018年4月11日 星期三

Spring Data JPA 的 JOIN 操作

其實在 Spring 上要做 JOIN,還挺麻煩的…
如果可以的話,我覺得能不要做就不要做比較好 XD

不過真的需要的時候,如果是需要 SELECT 時做 JOIN,推薦可以參考 [1-2] 的說明。
實務應用的話,假設以 logback 輸出到 SQL Server 的格式作為範例
想要一次取得整個 log 包含相關的 property 和 stack trace,以下的 Entity 範例是能夠套用。

logback 輸出 SQL Server 的 schema

schema 可以參考 [3],以下為 schema 的內容:

CREATE TABLE logging_event 
  ( 
    timestmp          DECIMAL(20) NOT NULL,
   	formatted_message VARCHAR(4000) NOT NULL,
    logger_name       VARCHAR(254) NOT NULL,
    level_string      VARCHAR(254) NOT NULL,
    thread_name       VARCHAR(254),
    reference_flag    SMALLINT,
    arg0              VARCHAR(254),
    arg1              VARCHAR(254),
    arg2              VARCHAR(254),
    arg3              VARCHAR(254),
    caller_filename   VARCHAR(254) NOT NULL,
    caller_class      VARCHAR(254) NOT NULL,
    caller_method     VARCHAR(254) NOT NULL,
    caller_line       CHAR(4) NOT NULL,
    event_id          DECIMAL(40) NOT NULL identity,
    PRIMARY KEY(event_id) 
  ) 

CREATE TABLE logging_event_property 
  ( 
    event_id          DECIMAL(40) NOT NULL, 
    mapped_key        VARCHAR(254) NOT NULL, 
    mapped_value      VARCHAR(1024), 
    PRIMARY KEY(event_id, mapped_key), 
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id) 
  ) 

CREATE TABLE logging_event_exception 
  ( 
    event_id         DECIMAL(40) NOT NULL, 
    i                SMALLINT NOT NULL, 
    trace_line       VARCHAR(254) NOT NULL, 
    PRIMARY KEY(event_id, i), 
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id) 
  )
Entity

假設我的 Entity 物件名字是 Activity、ActivityProperty、ActivityStackTrace,分別定義如下:

Activity

@Entity(name = "logging_event")
public class Activity implements Serializable {
  @Id
  @Column(name = "event_id")
  private Long eventId;
  
  @Column(name = "timestmp")
  private Long timestmp;
  
  @Column(name = "formatted_message")
  private String message;
  
  @Column(name = "logger_name")
  private String logger;
  
  @Column(name = "level_string")
  private String level;
  
  @Column(name = "thread_name")
  private String thread;
  
  @Column(name = "reference_flag")
  private Integer reference;
  
  @Column(name = "arg0")
  private String arg0;
  
  @Column(name = "arg1")
  private String arg1;
  
  @Column(name = "arg2")
  private String arg2;
  
  @Column(name = "arg3")
  private String arg3;
  
  @Column(name = "caller_filename")
  private String callerFile;
  
  @Column(name = "caller_class")
  private String callerClass;
  
  @Column(name = "caller_method")
  private String callerMethod;
  
  @Column(name = "caller_line")
  private String callerLine;
  
  @OneToMany(
      mappedBy = "eventId",
      cascade = CascadeType.ALL,
      fetch = FetchType.EAGER)
  private List<ActivityProperty> properties;
  
  @OneToMany(cascade = CascadeType.ALL)
  @JoinColumn(name = "event_id", referencedColumnName = "event_id")
  @LazyCollection(LazyCollectionOption.FALSE)
  private List<ActivityStackTrace> stackTraces;
}

ActivityProperty

@Entity(name = "logging_event_property")
public class ActivityProperty implements Serializable {
  private static final long serialVersionUID = -6532428503805543364L;

  @Id
  @Column(name = "event_id")
  private Long eventId;
  
  @Column(name = "mapped_key")
  private String key;
  
  @Column(name = "mapped_value")
  private String value;
}

ActivityStackTrace

@Entity(name = "logging_event_exception")
public class ActivityStackTrace implements Serializable {
  private static final long serialVersionUID = -50856985039937262L;

  @EmbeddedId
  private ActivityStackTraceId id;
  
  @Column(name = "trace_line")
  private String line;
}

ActivityStackTraceId

@Embeddable
public class ActivityStackTraceId implements Serializable {
  private static final long serialVersionUID = -4496556483758827756L;

  @Column(name = "event_id")
  private Long eventId;
  
  @Column(name = "i")
  private Integer i;
}

其中在 Activity 要連結 ActivityProperty 和 ActivityStackTrace 時,都是以 @OneToMany 來標注
用來表示一個 Activity 可以有多個 ActivityProperty 和 ActivityStackTrace。
此外,由於 ActivityStackTrace 並非是單一 ID,而是 Composite Primary Key 的形式
因此要利用 @Embeddable來組合。這部份可以參考 [4]。
同時,使用了 @Embeddable 之後,如果 JOIN 操作依然使用 mappedBy 的話,會出現 [5] 提到的錯誤
目前其實還沒有搞很懂這個錯誤是什麼意思,不過姑且使用 [5] 的作法,改成用 LazyCollection 可以解決。
未來如果有把問題搞懂的話,再回來追加補充了 @@a

除此之外,還得稍微注意一下,上述的 JOIN 寫法,有可能導致 [6] 描述的問題
也就是當要做資料刪除時,Hibernate 產生的 SQL Statement 會用很沒效率的方式在刪除關聯
而且某些狀況下有可能會刪除沒有預期要刪除的資料。

參考資料
  1. JPA,在@OneToMany里加入mappedBy属性
  2. hibernate基于注解的维护权反转:@OneToMany(mappedBy=)
  3. mssql.sql
  4. The best way to map a Composite Primary Key with JPA and Hibernate
  5. Hibernate cannot simultaneously fetch multiple bags
  6. Why you should avoid CascadeType.REMOVE for to-many associations and what to do instead
  7. @JoinColumn 详解

QuerySyntaxException: Path expected for join

參考資料
  1. HQL ERROR: Path expected for join