文章列表

測試驅動開發 | 系統邊界Mock

科技新知
MacauYeah・2024-04-23

好一段日子之前,筆者就介紹了一些寫Test Case的大方向 。對於大部份情況來說,有分隔的開發環境,有整個配套,測試起來是順暢的,想做單元測試可以,做整合測試也可以。但如果沒有,我們其實也要想辦法寫Mock。 Mock這個概念,對於寫前端程式的朋友應該比較熟悉,因為前端開發者總不能等後端準備好之後,才開始慢慢設計。前端很早期就要模擬一些情況,做介面設計,做各種思考。而且這個Mock不是指在運行單元測試時,才使用的臨時修改隨機數據。而是針對開發時,自行模擬的後端或外部環境。不過因為前端介面涉及很多主觀設計,很多元素冇辦法做固定的自動測試,所以前端的測試通常要人幫測試。 而後端開發,邊界Mock這一概念也很有用。在外部環境不足的情況下,為自己系統的邊界部份自建一個Stub / Dummy 等的模疑數據,是很有幫助的。不論我們對外部環境的掌控度有多少,我們走測試驅動開發(Test Driven Development),好好地定義這個外部環境的期待行為是很重要的。 例如,你有個功能,需要存入數據,但資料庫未準備好,也沒有所謂的In Memory資料庫可以用。這時,自己架空寫一個什麼都不做或回傳固定結果的函數作為中轉接口,然後在你的Test Case可以規劃你的想要結果。 也許你會說,這個函數就是存下資料,我不會需要它的回傳結果,但我們其實還是可以在Test case 中定義一些錯誤檢測,確保這個函數沒有Throw Exception 。再進一步想,我們主程式是否真的不負任何儲存失敗的責任?要定義其他回傳變數,方便寫Log讓追蹤?或者我們至少要知道成功後的Primary Key ?若然業務上真的不在乎儲存結果的有效性,我們不存入數據也是可以的? 所以歸根究底,我們還是在乎儲存的成功與否。還是有必要去驗證驗寫入是否成功。 上述例子,因為資料庫不存在,開發途中可能Test Case 有好長一段時間也通過不了,但至少當資料庫完備後,可以直接驗證,不用人手手工測試。 舉另外一個例子,我們要從某個地方,例如API或資料庫,讀取數據。我們也可以先寫中轉接口,並為它寫Test Case定義應有的行為。雖然明明就只是讀取,我們沒法控制太多。但在接口做好異常狀態處理,是很重要的。例如Handle exception、檢查某些重要業務值會不會是空、確保後續部份可以正常使用,這是因為我們不能被外部系統的失誤而導致自身系統癱瘓。 其實測試驅動,本質上就是強逼大家想多一點,好好定義預期的行為,不論內部條件怎樣變化,都有一自動的檢收標準。

Spring Boot 03 - 做好Database的模組化及測試用例

科技新知
MacauYeah・2024-04-12

這節,我們將會使用spring-data-jpa,寫一個業務上的資料庫模組,提供資料表的存取,讓你的好同僚可以直接使用。這樣可以在多模組的環境中,減少同一個資料表在不同地方重複又重複地重定義。將來要更新,也可以使用jar檔的方式發佈。 下戴模版 我們跟上節一樣,使用Spring Initializr (Maven) 下載模版,但細節筆者就不再講啦。Dependency主要選擇 H2 Database Spring Data JPA 對pom.xml作一些微調,並把spring-boot-start-data-jpa,h2改為只在測試中生效。 並把Java檔案搬一搬位置 # old location src/main/java/io/github/macauyeah/springboot/tutorial/springbootdatatest/SpringBootDataTestApplication.java src/main/resources/application.properties # new location src/test/java/io/github/macauyeah/springboot/tutorial/springbootdatatest/SpringBootDataTestApplication.java src/test/resources/application.properties 以上的操作,主要是因為我們的目標是提供Schema,或者叫資料表規格。其他用於做連線的操作,我們不需要打包在jar內。所以把那些次要的東西都放在test資料夾中。我們這時可以先用mvn test指令,確保一切功能還是正常。 Entity folder 然後我們入正題,在pom.xml中加入hibernate-core,spring-data-jpa, 然後在main資料夾下加入 Entity、Repository,例如前述用過的Apple和AppleRepo,最後資料夾就像是這樣。 . |-- pom.xml |-- src | |-- main | | `-- java | | `-- io | | `-- github | | `-- macauyeah | | `-- springboot | | `-- tutorial | | `-- springbootdatatest | | |-- Apple.java | | `-- AppleRepo.java | `-- test | |-- java | | `-- io | | `-- github | | `-- macauyeah | | `-- springboot | | `-- tutorial | | `-- springbootdatatest | | |-- SpringBootDataTestApplication.java | | `-- SpringBootDataTestApplicationTests.java | `-- resources | `-- application.properties 然後我們在Test Case中使用AppleRepo @SpringBootTest class SpringBootDataTestApplicationTests { @Autowired AppleRepo appleRepo; @Test void contextLoads() { Apple apple = new Apple(); apple.setUuid(UUID.randomUUID().toString()); apple.setWeight(100.0); apple.setGravity(1000.0); appleRepo.save(apple); } } 這個跟前述02-spring-data-jpa最大的差別,就是我們的main中只有Entity相關的Class,我們發佈jar,別人引用我們的class,別人不會解發其他不相干的商業邏輯。假如發佈02的例子,因為Spring有自動初始化Component的原因,很可能會誤觸發02中的BasicApplicationRunner.java Source Code spring boot data test

Git Worktree

科技新知
MacauYeah・2024-04-09

看了Git 大神的影片 part two,才知道原來切換git分支還是有不同的做法。傳統中,我們使用git checkout BRANCH_NAME_1 來切換到我們想要的分支。通常這樣做,代表我們放棄原來的工作環境,換到另一個工作環境中。 這樣做很好,對不對? 是的。但有些時候,我們只是被逼離開原本的工作環境,跳到一個過去的分支/節點去查一些東西,或者修正一些東西。更什的是我們原本的工作環境都還是混亂狀態下,我們不想做commit(提交),我們只好用git stash,暫時將工作環境存起,然後再git checkout BRANCH_NAME_1。在你想做的事做完後,再git checkout OLD_BRANCH。 看起來其實也沒有很麻煩,是不是? 但其實當你的專案有一定大小,你在不同版本跳來跳去,你的IDE就會不斷地重新編譯。更不幸的是,當你的不同版本中有模組數量的差異,弱一點的IDE,什至會攪死它的cache,之後就會發生鬼打牆。為解決IDE引發的問題,筆者有時會直接cp -r YOUR_PROJECT TEMP_PROJECT,在一個新資料夾下另起爐灶。那就是有兩個不同的資料夾裝載著你的專案。 這樣應該沒有問題了吧,是不是?這次是真的可以了,扣除了筆者個人健忘的問題,就沒什麼問題了。 不知大家有沒有經驗,連續commit了幾次,但最後一次commit卻忘了push(與伺服器同步),然後就跳到其他地方繼續工作。如果我們在同一個git repository下,我們commit了但忘了push,即使我們git checkout去了其他分支,用git GUI畫出commit graph時,也至少可以提醒筆者有一個未與伺服器同步的分支。但如果當初我們用的是cp,那就沒戲唱了,什至乎當初複制了去哪裏都忘了。(當你老闆同時要你跟多個專案,健忘真的很容易發生。) 這問題有解嗎?有的,git在2.5版本以後,就提供了一個git worktree的指令。它有點像cp 指令,更重要的是,它打通了兩個資料夾下的隱藏資料庫.git,當大家在那兩個資料夾底下,都可以看到另一方的存在。大家可以用git branch -a或git log --oneline --graph來看看。 詳細的指令介紹:git worktree git 大神的影片 Part 2

Git: 何謂MONO Repository

科技新知
MacauYeah・2024-04-02

之前看了一位git大神的演講,提及一個叫MONO Repository的使用情況。後期找資料之後,才發現到這是一個公司成長後的一個重大的挑戰。 何謂MONO Repository git的傳統,就是為每一個獨立的專案,建立一個新的Repository (中譯:倉庫)。這個很直觀,獨立專案,獨立管理。從零開始有很多好處,Repo體積通常會小一點,因為其內的東西都是緊密相關。做更新處理時,維護人員也更清楚自己的影響程度。這種架構方式,就叫Multi Repository。基本上,大家預設也是會走這個模式。 但當公司規模一直變大,多個專案可能不再獨立,各個專案或多或少都有一些關聯性。當任一專案更新,都有機會影響到其他人。如果公司使用Micro Service (微服務),就更有機會提早遇到。每次更新時,要跨專案地找出影響範圍原本就已經不容易,現在每個專案獨立地存放在不同的倉庫中,每個倉庫的更新速度不一樣,想要找到合適的地方、合適的時間點推出更新,更是困難。 所以,就有公司就提出,將所有專案都放在同一個Mono Repository中,方便用工具去檢查更新影響。相比Multi Repository,這樣做還可以保證同一個改動可以發生中同一個Commit中,可以讓跨專案的團隊可以即時合作(強逼修改別人的專案)。但這樣使一定會有很技術問題出現。跨專案團隊不可能每個專案都熟悉,因為不熟悉而引起的副作用一定會有,所以Main / Master分支出現有缺陷的機會提高了。亦有人提出,使用Mono架構,還必要使用trunk base分支模式。也就是那些新功能,雖然要創建分支開發,但亦要盡早整合到Main / Master中。這才能讓不同的團隊盡早知道問題,並解決問題。 除了開發模式更具挑戰外,Mono架構對git的效能也有很大影響。因為多專案混合,Repository的大小基本都會很大。每個git指令都會變慢,所以必需做一些週期性的cache,讓git graph, git status這樣日常操作變得暢順。同樣地,持續整合/發佈需要作出調整。不過這些筆者就不在這邊詳述了,有興趣朋友可以到git 大神的Youtube觀看。 So You Think You Know Git - FOSDEM 2024 註:據筆者的資料搜集,很多大公司(Software龍頭)都有使用Mono Repository去做集中管理。只不過筆者不知道如何Fact check,就不在這裏提了。

Docker環境參數化 - Arg VS Env

科技新知
MacauYeah・2024-03-26

Docker Variable control 我們在Docker Image的打包時,最簡單當然就是每個步驟都使用最新版本。例如Docker Base Image,大家可能選用latest tag,安裝linux package (Linux包),也可能就apt install / yum 安裝最新的穩定版本。但如果我們想要更好地做測試,就要使用指定版本,方便追蹤問題。而Docker在打包和運行時,都有不同的方式讓大家定義或覆寫指定參數。 Docker build arg 我們先從打包Image開始。 例如我們需要使用一個Base image為 ubuntu,版本預設為22.04,但有需要時可以經build指令覆寫,可以這樣寫 ARG ubuntu_version=22.04 FROM ubuntu:${ubuntu_version} # default ubuntu_version=22.04 docker image build -t test2204 ./ # or overwrite by --build-arg docker image build -t test2404 --build-arg="ubuntu_version=24.04" 雖然Dockerfile的RUN指令都是使用linux shell,但在Dockerfile中想表達條件控制(if else statment)就不太易看。在外部加入script做控制,是另一個可行的後備選擇,它更可以連image名字也進行參數化。 # in bash script, you also can if [ $beta == true ] then ubuntu_version=24.04 else ubuntu_version=22.04 fi docker image build -t test:${ubuntu_version} --build-arg ubuntu_version=${ubuntu_version} Docker Container Run and Docker Compose 一般來講,Linux Container 在執行時,就等於進入Linux Shell。也就是,我們可以使用Shell中的環境變數。 我們在打包Image前,已經可以在Dockerfile中定義自己的ENV數參(也就是環境變數)。與前面的Build Arg有所不同的是,ENV是定義在Dockerfile中,在Container運行時以環境變數的形式存在,它也可以在運行中被改變。而Arg,則只在打包Image時存在,運行期間就不存在了。(當然,你在打包時,用Arg傳入Env,以運到這個目的。) 另一個更特別的性質是,那怕ENV沒有定義在Dockerfile中,我們運行時也可以加入更多的環境變數,大家就當成是一般Linux操作,隨時在自己的shell中加入變數。 # -e, --env for inline variable # --env-file for file docker container run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash 同樣地Docker compose,也支援環境變數。筆者建議environment可以使用Array格式,日後可以更方便地直接改為env_file。 # docker-compose.yaml services: ubuntu: image: ubuntu:22.04 environment: - RACK_ENV=development - SHOW=true - USER_INPUT 上述的寫法沒有任何問題,不過如果你的docker-compose.yaml是放在git等版本控制中,你更新環境變數就有可能會影響到其他人,這時你就會想轉成env_file。 docker-compose.yaml預設就會讀當前資料夾的.env,就算不存在,也可以正常運行。(當然,大家的Image/Container應該要有預設值) # docker-compose.yaml services: ubuntu: image: ubuntu:22.04 # if env_file is not defined, it will load .env. # or you can load the specific file. # env_file: # - ./a.env env_file內,每一行就是一個變數 # .env or a.env RACK_ENV=development SHOW=true USER_INPUT 使用預設的.env還有一個好處,就是我們可以把docker-compose.yaml也變成受環境變數控制。 # docker-compose.yaml with variable control, only works in default .env services: ubuntu: image: ubuntu:${ubuntu_version} # .env ubuntu_version=22.04

Git - 持續整合策略 02 | Git - Continuous integration strategy 02

科技新知
MacauYeah・2024-03-14

Night Build 實務操作上的注意點 Night build第一個要注意的問題,就是要確保同一個commit,真的可以重複建設。一般來說,大家的目標只在運行測試,而自動測試不具破壞性,就基本可以重複的。而如果測試當中包含發佈測試版本,那就還要考慮重複發佈有沒有生效或造成附作用。 以Java maven為例,重複發佈測試版本需要遵守特定的規則,版本號需要以SNAPSHOT結尾,這是為讓maven每天都會重新下載它們的包。而沒有SNAPSHOT結尾的,就只會做一次性下載,減少重複下載造成的資源浪費。若真遇著不支援重複發佈的情況,就需要以日期時間做版本號,就像vscode的某些插件,就是以時間截結尾以作為區分。 Night build另一個要注意的問題,就是開發圖隊何時進行下一輪開發,這會決定何時有新的版本號。扣除上述因為工具不支援的而引發的副作用,還要考慮沒有更新而發生的問題。 有個尷尬情況是,團隊在發佈現行版本時,release commit與main有機會是同一個commit(也就是未有進行下一輪開發)。若不斷重複發佈,有沒有變相發佈了一些沒有預期的功能?例如Docker image,官方大力建議每日自動發佈。當底層的image更新後,頂層引用它們的image,也可以重新發佈,保持安全性。但這樣做的問題,就是頂層的同一個版本號,昨日與今日的運行結果也可能不一樣。這對追蹤問題,並不友好。 所以大家做分支整合時,要預先對版本號作好規劃。然後還要留意Night build不應與release commit重疊。版本號大家做好語意管理,再加上alpha / beta / SNAPSHOT等區分Night build版本,應該就足夠了。而commit重疊問題,就要留意開發週期,Night build要麼就比release早一個commit(即在release時,不推進Night build),要麼晚一個commit(即馬上規劃下一個版本號進行Night build)。

星穹鐡道:抽角色、組隊簡介

手機‧電玩
MacauYeah・2024-03-13

因為抽卡機率問題,坊間很多建議都基於課金的前題,不是所以有人都可以重複。但這亦不是筆者體驗這遊戲的主要方向,所以筆者集中分享一些主線必定會取得的角色,或盡量以4星的方式組隊。但在說明組隊之前,先講一講基本系統。讓大家知道那些地方有課金機率成份。 基本出戰 本遊戲是團隊戰,最多同時4名角色上場,同一角色不會出現兩次。影響出戰強度的,除了角色本身屬性、技能,還有裝備要求。同一角色可以裝備一款【光錐】,六款【遺器】,達到不同的Buff。 每個角色獨有自己的【命途】技能養成,光錐及遺器則可以交換使用。 在攻略副本時,可以借好友的角色,但依然會限制同一角色不能重疊。而好友的光錐及遺器不能交換。 卡池 在遊戲中有角色【躍遷】,就是抽角色的地方。除主角外,其餘角色都可以經【躍遷】以機率的方式抽取。若抽到重複角色,會轉化為【星魂】,用作提升角色的特殊技能。主角的星魂以遊戲進度獎勵發佈,其他角色暫時都以抽取為主要來源。特殊角色,如【黑塔】有特別支線任務可以取得。 光錐同時在【躍遷】中取得。 遺器則是副本敵人隨機掉落,沒法經抽選擇。而角色命途技能、光錐、遺器養成部份,所需資源都可在遊戲主線或副本取得。所以有限抽取的,就是角色和光錐。 希有度 角色稀有度最低為4星,最高為5星,道具則為有3-5星。 卡池沒有4星角色保底,只有4星結果保底,每十連抽可以得到4星角色或道具。 組隊目標 - 4星非洲隊 主線故事中,一定可以獲得 主角(物理,攻)或(第一章最後獲得,火,盾) 三月七(冰,盾) 艾絲妲(火,輔助Buff) 丹恆(風,單體攻擊) 娜塔莎(第一章中後期獲得,物理,奶) 黑塔(支線模擬宇宙獲得,冰,群體攻擊) 艾絲妲原本筆者也以為是抽角隨機獲得的,但以BiliBili Wiki引證,其實是抽角教學中必定獲得的角色,所以道理上各位也一定會有。但有些版本有活動送角色,但似乎地區不一樣有不一致情況,故筆者沒有列出。 三月七 雖然在取得奶之前,主角(物理)、三月七、丹恒、艾絲妲,就是沒有選擇之下的選擇。但其實三月七的盾有隱藏技能,會增加受擊機率,這是變相指定角色吸仇恨換賺能量的做法。她也是在缺奶時最重要假回血手段,所以是一個有長期培養的角色,用來湊雙冰、雙盾或一奶一盾也不錯。 艾絲妲 筆者一直忽略了的一位重要角色,因為她施放攻擊就有機會蓄能,蓄能全體加攻。終結技有全體加速效能,普偏的裝備方向是為她加速加能量,讓她可以再為其他人加速加攻。也因為第一章後,與主角可以組成雙火隊,主角可以全隊加盾,艾絲妲加速,打火弱點敵人的話,一定不虧。 娜塔莎 奶,就不用說明了。另外,她也作雙物理的組成。也是筆者作為平常無腦開荒的組成,主角(火,盾) 加 三月七 加 娜塔莎 加 弱點輸出。效率可能不高,但勝在無腦。 無腦隊最大的問題是弱點擊破率很低,因為盾和奶都需要經常回復,少了輸出的機會成本。

Spring Boot 02 - 快速接入Database的選擇: Spring Data JPA

科技新知
MacauYeah・2024-03-08

快速下戴模版 使用Spring initializr,可以很容易就建立一個以Spring boot starter為底的java project。大家可以使用Spring 官網又或是vscode plugin 快速地建立一個maven或gradle project。筆者較為熟悉maven,就以maven起一個範例。 在使用Spring initializr有幾件事必需要指定的: Spring boot version: 3.x.y 或以上 Language: java Group Id: 請選擇有意思的域名,如果你用github,可以選 io.github.yourusername artifactId: 這個範例的名字,例如commandline Packaging type: 本次使用jar,日後若開發web 應用,可以使用war Java version: 17或以上 Dependency: Spring Data JPA, Spring Boot DevTools 這次不像過去順利,因為這裏欠缺了Database連線資料,為了方便測試,我們先在pom.xml加入 h2與spring的整合很好。即使用什麼都不設定,直接運行mvn spring-boot:run,都可以成功執行了。但如果可以,在application.properties加入資料庫設定,會方便日後移植到其他常用的資料庫品版牌。 # src/main/resources/application.properties spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:mem:testdb; spring.datasource.usename=random spring.datasource.password=random 然後我們就可以做靠Spring Data JPA去生資料庫的表 (table)。Spring Data JPA預設使用的是Hibernate。假設,我們有一個表叫APPLE。我們就可以開一個class Apple和一個interface AppleRepo去接它。 // src/main/java/io/github/macauyeah/spring/tutorial/springbootdatabasic/Apple.java @Entity public class Apple { @Id String uuid; Double weight; // getter setter } // src/main/java/io/github/macauyeah/spring/tutorial/springbootdatabasic/AppleRepo.java public interface AppleRepo extends JpaRepository{ // no content here } 注意,因為不同需要,AppleRepo可能繼承不同的XXXRepository,它們大部份都是用來觸發寫入資料庫的指令。而這個也晚除了直接存取Hibnerate EntityManager的需要。 亦因為我們現在用的是h2Database,其實資料表並不存在。我們需要在執行Spring Boot時,同步先建立表,所以在application.properties 加入自動建表的設定。 # src/main/resources/application.properties spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=update 然後在Spring Boot Context的環境下,可以隨時執行寫入的操作。 @Autowired private AppleRepo appleRepo; public void saveApple() { Apple apple = new Apple(); apple.setUuid(UUID.randomUUID().toString()); apple.setWeight(100.0); appleRepo.save(apple); } Source Code spring boot data basic 因為h2Database只是用作測試用,所以spring-boot執行完,資料庫就會被刪除。而上述原始碼當中,還附上了一些dump sql的方法,至少可以讓大家驗證己儲存的結果。

如何衡量課金制遊戲的價值

手機‧電玩
MacauYeah・2024-03-05

筆者因為作息調整,可以花時間花資源去深玩的遊戲越來越少,需要專攻一款價值高的遊戲是一個很重要的課題。隨着年紀增長,家庭環境改變,大家都可能會遇到相同的問題。所以筆者很想探討一下,一年只玩一款遊戲的話,CP值是否有所保證?也就是滿意度和支出的比例是否保持一個高水平? 好多老一派玩家會支持傳統主機遊戲,主要係因為免費課金制,品質很差。初時下載遊戲免費,但遊戲無法通關,過程也很重複無趣,所以滿意度很差。正好筆者最近重回手遊,就來分享一下時間和滿意度比例。 本文為了方便討論單一手遊的價值,先只以「不課金」,只討論時間成本支出。日後再以「課金上限」來對比不同的課金情況或是與主機遊戲對比。 定義 成本:時間 CP值 ⇒ 淨滿意時長 / 淨成本 ⇒ 即成本越高,每單位成本的滿意度越低。 CP值 = sum (分段內容時長 * 分段滿意度) / (時間 ) 註:分段滿意度可能為負,為方便倍數計算,最大為10,最少為-10。 崩壞:星穹鐵道 - 主觀評分 主線序+ 第一章:20小時 * 8 = 160 五角色養成,累積前70等的升級素材:30小時 * 2 = 60 因為很多時候都是內卦刷戰鬥,不怎開心,但還未至於要吐 第二章:15小時 * 8 = 120 筆者有幸以不課金的陣型,以完成主線第二章。主角(火)、娜塔莎(物理)、希露瓦(雷)、景元(雷,劇情指定角色)。除了主角滿級其他都很素。 角色養成,累積70-80(封頂等級)的升級素材:30 * 1 = 30 70-80等級就開始跳躍性質變,借助外援也無法快速囤積資源。 筆者只有主角的等級+存護命途可以練滿,另一角色也只有等級練滿。其他連突破70級的資源都不夠。 模擬宇宙部份挑戰:5小時 * 8 = 40 忘卻之亭部份挑戰:1小時 * 2 = 2 有難度,但沒什麼樂趣 淨滿意度·時長 :160 + 60 + 120 + 30 + 40 + 2 = 412 淨成本:20 + 30 + 15 + 30 + 5 + 1 = 101 CP值 412/101 = 4.08 以上,就是星穹首年來的內容,對應六季的更新。目前遊戲新剛推出2.0更新,筆者也會花一點時間了解一下是否有等級門檻。 不過以長期遊玩的角度,還要考慮如何提升高等級刷素材的滿意度。

Git - 持續整合策略 | Git - Continuous integration strategy

科技新知
MacauYeah・2024-02-23

對於原始碼的管理,平常筆者也有在用gitlab的Continuous integration,針對每次提交(commit),都會有自動編譯和測試。但當一個專案中,有很多關聯庫(dependency library)的引用時,光是專案中每個commit 行auto build就不夠用了。更嚴重的是,若然大家有很多微服務micro service,它們的更新不會反映在commit中。 所以定期重跑動動編譯和測試,是筆者認為可以緩解關聯更新的問題,至少可以提高知道問題所在。 筆者先做了一些功課,參考別人怎樣思考Night build (定期重新編譯)這件事。 每次整合新功能到穩定分支(stable branch)之前,都需要做自動測試。 當專案複雜性越來越大,每次自動測試都把全部測試跑一次,就會遇到效能瓶頸。 所以考慮commit時做單元測試(unit test),然後每個固定的時間問隔做整合測試(integration test)。那個固定的時間間隔就是Night build。 而筆者的問題並不是來自於效能瓶頸,而是涉及關聯性更新問題。這些要麼就有是經code base 層面引發關聯性自動試測,要麼就是Night build重複測試。這兩個功能,gitlab都有提供,只是筆者初步構想下,Night build比較易設定。因為要考慮micro service的於沙盒環境的部署,最簡易的Night build只需要一個共用的環境就夠。但也同樣意味著,Night build需要進行多個不同的分支測試。就需要多個不同的環境。 Night build的測時時機也是一個問題,因為測試當下,並不能百份百對應關聯micro services的提交狀況,大家就更需要做好發佈的版本號語意管理。 不知道看完筆者的策略之後,大家又有何看法?歡迎大家一起加入git筆記的編輯。

星穹鐵道:模擬宇宙系統簡介

手機‧電玩
MacauYeah・2024-02-22

因為模擬宇宙是遊戲中一個很重要的資源獎勵來源,所以就來全面的講講它的運作方式。 模擬宇宙玩法 模擬宇宙分為多個世界,目前筆者已知的世界有八個,分別對應遊戲序章、一章、二意的地圖和敵人,除了Boss外的敵人種類都有一定隨機性。第三世界至第八世界需要根據主線遊戲進度而開啟。 積分獎勵 每次挑戰不同世界都有積分獎勵,即使沒有到達終點也會有積分。第三世界開始有不同的難度選擇,積分當然也會因為難度提高而變得更高。 每週積分到達上限後,可以取得重要的行跡升級素材 - 【命運的足跡】。而模擬宇宙一週積分會重置一次,也就是一週可以取得一個。這是角色養成後期的必要道具,只能經過不同的獎勵途徑取得(例如無名勳禮、餘燼兌換,但因為週期更長,所以暫不推薦)。 敵人道具掉落 平常角色行跡升級素材,需要經過刷普通敵人取得,同樣原理,刷模擬宇宙不同世界也可以取得。最大差異是,普通敵人刷新率受時間限制,而模擬宇宙重新挑戰就可以一直刷。要變新角色的話,刷模擬宇宙是一個可行的選擇。 積分和道具掉落獎勵也當然會因為難度提高而變得更豐厚。 命途、祝福 在主線中,不斷地提及這個遊戲中很有多個古神之類的存在。而這些神明,在模擬宇宙中就以【命途迴響】、及【祝福】來為玩家加Buff。 玩家在進行世界時,選擇角色時,亦要選擇命途。然後每次戰鬥後,就可以在三個隨機祝福中選一個。當目標命途底下對應的祝福齊夠六個以後,就可以取得命途迴響,一個更強大的Buff。指定祝福越多,命途迴響有更多額外功能,所以模擬宇宙中的Buff有很大的隨機性。 我們要降底隨機性,就只能通過有限度的置換來選取掉落的祝福。 解鎖一定量的命途、祝福,也可以取得一次性的收集進度獎勵,所以去到第七、八世界,你就會開始糾結,該選新的祝福但沒有命途加成,還是先加成後收集? 奇物 更隨機的道具,筆者認為大部份都是Debuff,因為它很大機會嚴重地干擾命途和祝福的配搭。因為同樣有收集進度獎勵,所以第一次遇到的話,也是要取舍。 筆者建議就先收集,後通關。因為低機率的物品真的百年難得一遇。 沉浸獎勵 一定難度開始,在挑戰過程中會出現沉浸獎勵,可以使用【開拓力】或【沉浸器】來兌換。它也是取得後期遺器的重要來源。

git 分支整合問題

科技新知
MacauYeah・2024-02-20

不知道大家的開發團隊、專案規模有多大,但只要系統或程式已發佈,同時又要做維護更新,git 庫都至少會有兩條分枝: 新功能 - main / feature 最新的穩定發佈版本 - Release / v1.x.x 最好的情況下,在開發完新功能之前,穩定版本都沒有需要緊急修正的地方,開發者可以專心開發新功能(main / feature)。然而這個情況並不能經常維持。 情況1:有Bug要馬上修正 最常見到的情況,就是穩定發佈版本有瑕疵,可以經過小修小改來止血,由v1.x.x ⇒ v1.x.y,這些可能對用戶來說,是沒有太大感覺的改動。不過對於開發流程,就免不了由v1.x.y整合(merge)回main時,出現修改衝突的問題。 建議 若屬於日後不再需要的改動,不需於整合到main中, 當然什麼都不用做。但若屬於必要的更新,就需要早早整合到main中。整合雖然痛苦,但延後整合沒有好處。以筆者的經驗,每次整合時有衝突,而越早整合越有條件知道該取用自動混合的那個版本。以整合工具的語言來說,就是更容易的作出use mine / use theirs / edit。 情況2: 不同功能之間有衝突 上述情況1,已經算是可控的。主要因為穩定發佈版本都只會接受小修小改,大改都會直接在main中開當為新功能開發。當你有多個很重要的功能在不同時期被提出,而有些功能你沒有信心在下個發佈中提出,你就會選擇以獨立分支來實現不同的功能,最後選擇信心度高、權重也比較高的功能來發佈。這樣的好處是你可以有限時間先完成最必要的功能,但問題是多個功能分支之間,更容易地有衝突,後期也需要很廢心力地整合。 建議 少做資料夾層面的改動,因為git rename的功能並不是萬能的,會令很多git自動選擇版變得不可讀。筆者的經驗,就是錯把後端和前端的資料夾混在一起,令後端的一些重命名影響到前端。前端也因為有重寫的需要,對資料夾結構大改。最後結果就是很多看不懂的git自動選擇版。有一些有選對,但有一些就選錯。 可以做一些事前處理,來減經痛苦。在筆者的資料夾問題情境,在把後端將要整合的多個commit中,挑選最早前沒有命名問題的commit先整合一次。然後前端先手動模擬後端的人工命名,自行commit一次,最後再把後端剩餘的commit再做整合。這個做法不是完全解決問題,但至少可以讓use mine / use theirs / edit更新易理解。 而另一個建議是,縮短發佈週期,逼使其他開發中的功能越早做整合,也逼使每個功能不要做太大規模的改動。如果真的做大規模改動,就要有心理準備要多次重要的整合。 情況3: 多個穩定發佈版本需要同時維護 若然大家面對的工作規模真的很大,同時有多個版運行版本,就如gitlab,每一個月都有一個新功能版本(16.0.x, 16.1.x, 16.2.x,… 16.9.x),但它不會強逼大家更新,對於過去一段時間的功能版本,也會推出安全性更新(前述的x會不斷修正問題)。 這是一個很負責任的發佈模式,不過對於開發者來講就一定很地獄。因為16.0.x的安全更新並不能無痛地整合到16.9.x中,可能每個版本重新人工修改還要來得穩健。 建議 各個分支人工修改可能更適合。最後就是取決於商業政策的考量,到底公司願意為已發佈的功能版本提供多久的支援。就以gitlab為例,其實它也只承諾維持兩三個月前的功能版本。是否會backport到多個月之前的版本,就看問題的嚴重性和backport難易度。 也分享一些筆者朋友的經驗,他們開發的是軟件跟硬件整理的軟件庫。但因為硬件有限制,例如庫的大小、算力的差異,所以最後分支多到爆炸。這也是軟硬整合的痛,問題暫時無解。除非老闆肯放棄市場。

星穹鐵道:中後期無課心得

手機‧電玩
MacauYeah・2024-02-08

分享前先講講為何會回鍋。因為筆者平常支持的RPG品牌Square Enix 實在不濟,每次更新完手機,它家的買斷型遊戲都攔淺。實在玩不下去,還是要回到米哈遊,星穹有中文、持續更新、自動存檔,從內容到技術上都比買斷型遊戲更適合手機遊玩。所以筆者就繼續來養成看看吧。 角色取得策略 之前筆者在其他文章內有提及過,免費課金制最大的痛點就是可取得的資源有上限,所以到了中後期,資源投資在什麼角色上是一個很重要的考量。但由於本文的主題是無課心得,所以筆者亦限定自己只使用免費卷或免費石抽4星角色。筆者的抽角色策略也很保守,各位應該可以很輕易地重複到。最簡單地講,只有弱點擊破效率不足時、打不過的主線時,才會考慮抽角色。 青雀 - 量子屬性 - 智識命途 主線第二章,豐饒玄鹿,弱點屬性為火、冰、量子。主線預設只有一火(主角)一冰(三月七)的組合,筆者實在打不過豐饒玄鹿。此時有兩個選擇,要麼就多抽一個量子角色,豐富一下隊中角色;要麼去打支線模擬宇宙,取得另一隻冰免費角色-黑塔。 為長遠發展,筆者先抽角色,不過運氣不怎樣,只抽到青雀。智識命途主打群體傷害,但她沒有持續傷害加成,而且戰技有運氣成份,所以強度不高。但對於主線玄鹿至少可以一戰,有抽到朋友可以隨便升一升等級就可以。 希露瓦 - 雷屬性 - 智識命途 在中期開始,除了經普通遇敵獲取【行跡素材】之外,攻略支線【模擬宇宙】也是取得【行跡素材】的重要方法。此時希露瓦配搭虛無命途的【懷疑】debuff效果可以在【模擬宇宙】達到多重疊加傷害。有條件疊加傷害的另一個四星角色應該是素裳。筆者有抽到,但因為物理屬性重疊,所以沒有練。 另外,在正常情況下,在攻略完第一章後,預設會有一火(主角)一冰(三月七)一奶(娜塔莎),再加上希露瓦,【均衡試練伍】可以穩過。通過後,可以特破角色等級上限至80,是1.6版本的封頂等級。(2.0版本才剛更新,但應該沒有調整封頂等級)

龐大的Docker Logs該如何處理? | 傳統的syslog幫到你

科技新知
MacauYeah・2024-02-02

平常大家在做單機app時,寫log有很多選擇,最簡單就是寫在檔案中。但在docker container裏面,寫檔案時要注意怎樣保留log檔,避免因為重建container時不見了。 docker 大部份官方預設image,都把log導向至stdout和stderr。這是方便docker做管理,也方便大家使用統一的docker logs指令來查看,即使到了Swarm mode底下,docker service logs也是同樣原理,使用差異不大,頂多就是不保證log的實時性。 如果網路延遲不計較的話,最大問題也是logs怎樣保存的做法。預設就是container刪走的時候,logs也會一借走。單機模式下,沿用最普遍的方法寫log的做法不是不可行,只是考慮到在極端情況下,同一個node(節點)中,有可能同時運作同一個service(服務)的多個分身(replica),這裏它們寫檔案時就有機會互相搶佔。 筆者認為,比較合理的是外部提供的服務,例如syslog,把寫檔的操作交給節點的Host OS處理。然後就保證好每筆log都會是一條完整的記錄。 以下就以linux Host裏面的syslog,為大家簡介一下設定的步驟。 設定docker 導向 syslog 把該主機的docker daemon (/etc/docker/daemon.json),設定使用syslog driver,並以特定的方式編寫syslog tag。 { "log-driver": "syslog", "log-opts": { "tag": "dockercontainer/{{.ImageName}}/{{.Name}}/{{.ID}}" } } 無腦設定已完成,重啟docker就可以了。 但為了日後管理方便,能把docker log放進獨立的一個檔案中,會更易找問題。所以我們可以進一步設定syslog。我們以Ubuntu 22.04為例,可以在/etc/rsyslog.d/下增加一個設定檔(/etc/rsyslog.d/*.conf),指定看到syslog tag以dockercontainer為首的記錄,都要獨立抽出來。 # file: /etc/rsyslog.d/51-docker.conf :syslogtag,startswith,"dockercontainer" -/var/log/dockercontainer.log 為免有檔案權限問題,手動指定檔案的所有權後,才正式重啟syslog。然後所有相關記錄都會寫在/var/log/dockercontainer.log 滾滾滾滾滾動的log檔 檔案一天一天地長大,如果可以,還是自動清掉太舊的記錄為妙。Linux Syslog,通常也會配著logrotate使用。 筆者亦以Ubuntu 22.04為例子,做了個最簡單的自動滾Log功能。目標就是當log檔案大於1M後,就要重開log檔。舊的log檔最多保留7份,多了就刪掉最舊的。 # file: /etc/logrotate.d/rsyslog-dockercontainer /var/log/dockercontainer.log { rotate 7 size 1M missingok notifempty compress delaycompress sharedscripts postrotate /usr/lib/rsyslog/rsyslog-rotate endscript } 加了設定後,什麼都不用重啟,因為它是Ubuntu 的排程動作,到執行時就會以最新的設定檔執行,詳見/etc/cron.daily/logrotate. 有需要手動測試的話,需要手動呼叫/usr/sbin/logrotate。加入-d參數後,會被視為debug mode,這是官方的說法,但因為debug mode沒有執行效果,更加像是linux中常見的dry run mode。