Master.go

package service

import (
	"fmt"
	"log"
	"os"
	"time"

	"saseul/config"
	"saseul/data/chain"
	"saseul/data/mainchain"
	"saseul/data/resourcechain"
	"saseul/data/tracker"
	"saseul/datasource/database"
	"saseul/datasource/poolclient"
	"saseul/staff/processmanager"
	"saseul/util/logger"
	"saseul/util/timer"
	"github.com/gorilla/websocket"
)

// Master κ΅¬μ‘°μ²΄λŠ” λ§ˆμŠ€ν„° μ„œλΉ„μŠ€λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
type Master struct {
	Iterate int
	Socket  *websocket.Conn
}

// NewMaster ν•¨μˆ˜λŠ” Master μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
func NewMaster() *Master {
	if processmanager.IsRunning(processmanager.MASTER) {
		logger.Log("Master process is already running.")
		stop()
	}

	processmanager.Save(processmanager.MASTER)
	tracker.Init()

	return &Master{
		Iterate: 1000,
		Socket:  createSocket(),
	}
}

// Init ν•¨μˆ˜λŠ” Master μ„œλΉ„μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³  ν•„μš”ν•œ μž‘μ—…μ„ μ„€μ •ν•©λ‹ˆλ‹€.
func (m *Master) Init() {
	t := timer.NewTimer()
	logger.Log("Master Process Initializing...")

	load()

	logger.Log(fmt.Sprintf("Master Process: Status Initializing Time: %.2fs", t.Check().Seconds()))

	go m.listen()
	checkProcess()
	checkDB()
	logRotation()
}

// createSocket ν•¨μˆ˜λŠ” μ›Ήμ†ŒμΌ“ 연결을 μƒμ„±ν•©λ‹ˆλ‹€.
func createSocket() *websocket.Conn {
	u := fmt.Sprintf("ws://%s:%d", config.MasterAddr, config.MasterPort)
	conn, _, err := websocket.DefaultDialer.Dial(u, nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	return conn
}

// listen ν•¨μˆ˜λŠ” μ†ŒμΌ“ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•˜κ³  μ²˜λ¦¬ν•©λ‹ˆλ‹€.
func (m *Master) listen() {
	for {
		_, message, err := m.Socket.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			return
		}
		handleMessage(message)
	}
}

// handleMessage ν•¨μˆ˜λŠ” μˆ˜μ‹ ν•œ λ©”μ‹œμ§€λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
func handleMessage(message []byte) {
	// λ©”μ‹œμ§€ 처리 둜직 μΆ”κ°€
}

// Main ν•¨μˆ˜λŠ” μ£Όμš” μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
func (m *Master) Main() {
	for i := 0; i < m.Iterate; i++ {
		time.Sleep(1 * time.Second) // ν•„μš”ν•œ 경우 μ‘°μ •
	}
}

// load ν•¨μˆ˜λŠ” 초기 λ‘œλ“œ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
func load() {
	processmanager.Spawn("SERVICE_BIN", "DataPool")

	for !poolclient.Instance().IsRunning() {
		time.Sleep(1 * time.Second)
	}

	// μ •μ±… μ„€μ •
	poolclient.Instance().SetPolicy("chain_maker", config.Consensus)
	poolclient.Instance().SetPolicy("resource_miner", config.Consensus)
	poolclient.Instance().SetPolicy("collector", config.Collect)
	poolclient.Instance().SetPolicy("peer_searcher", config.Collect)

	poolclient.Instance().SetPolicy("main_chain_waiting", false)
	poolclient.Instance().SetPolicy("resource_chain_waiting", false)
}

// stop ν•¨μˆ˜λŠ” μ„œλΉ„μŠ€λ₯Ό μ€‘μ§€ν•©λ‹ˆλ‹€.
func stop() {
	processmanager.Kill(processmanager.RESOURCE_MINER)
	processmanager.Kill(processmanager.CHAIN_MAKER)
	processmanager.Kill(processmanager.COLLECTOR)
	processmanager.Kill(processmanager.PEER_SEARCHER)
	processmanager.Kill(processmanager.DATA_POOL)
	logger.Log("The master process has stopped.")
	os.Exit(0)
}

// reload ν•¨μˆ˜λŠ” μ„œλΉ„μŠ€λ₯Ό μž¬μ‹œμž‘ν•©λ‹ˆλ‹€.
func reload() bool {
	mining := poolclient.Instance().GetPolicy("mining").(bool)

	fixedHeight := chain.FixedHeight()
	reloadPoint := resourcechain.Instance().Block(fixedHeight)
	logger.Log(fmt.Sprintf("Master: Reload - reload_point: %d", fixedHeight))

	poolclient.Instance().SetPolicy("chain_maker", false)
	poolclient.Instance().SetPolicy("resource_miner", false)
	poolclient.Instance().SetPolicy("collector", false)
	poolclient.Instance().SetPolicy("peer_searcher", false)

	processmanager.Kill(processmanager.RESOURCE_MINER)
	processmanager.Kill(processmanager.CHAIN_MAKER)
	processmanager.Kill(processmanager.COLLECTOR)
	processmanager.Kill(processmanager.PEER_SEARCHER)
	processmanager.Kill(processmanager.DATA_POOL)

	for processmanager.IsRunning(processmanager.RESOURCE_MINER) ||
		processmanager.IsRunning(processmanager.CHAIN_MAKER) ||
		processmanager.IsRunning(processmanager.COLLECTOR) ||
		processmanager.IsRunning(processmanager.PEER_SEARCHER) ||
		processmanager.IsRunning(processmanager.DATA_POOL) {
		logger.Log("Master: Reloading processes...")
		time.Sleep(1 * time.Second)
	}

	resourcechain.Instance().Remove(reloadPoint.Height + 1)
	mainchain.Instance().Remove(reloadPoint.MainHeight + 1)

	load()

	poolclient.Instance().SetPolicy("mining", mining)
	return true
}

// checkProcess ν•¨μˆ˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό ν™•μΈν•˜κ³  정책에 따라 μ‹œμž‘ λ˜λŠ” μ’…λ£Œν•©λ‹ˆλ‹€.
func checkProcess() {
	policy := poolclient.Instance().GetPolicy()

	if rmPolicy, ok := policy["resource_miner"].(bool); ok {
		rmRunning := processmanager.IsRunning(processmanager.RESOURCE_MINER)
		if rmPolicy && !rmRunning {
			processmanager.Spawn("SERVICE_BIN", "ResourceMiner")
		} else if !rmPolicy && rmRunning {
			logger.Log("Resource miner process end.")
			processmanager.Kill(processmanager.RESOURCE_MINER)
		}
	}

	if cmPolicy, ok := policy["chain_maker"].(bool); ok {
		cmRunning := processmanager.IsRunning(processmanager.CHAIN_MAKER)
		if cmPolicy && !cmRunning {
			processmanager.Spawn("SERVICE_BIN", "ChainMaker")
		} else if !cmPolicy && cmRunning {
			logger.Log("Chain maker process end.")
			processmanager.Kill(processmanager.CHAIN_MAKER)
		}
	}

	if colPolicy, ok := policy["collector"].(bool); ok {
		colRunning := processmanager.IsRunning(processmanager.COLLECTOR)
		if colPolicy && !colRunning {
			processmanager.Spawn("SERVICE_BIN", "Collector")
		} else if !colPolicy && colRunning {
			logger.Log("Collector process end.")
			processmanager.Kill(processmanager.COLLECTOR)
		}
	}

	if psPolicy, ok := policy["peer_searcher"].(bool); ok {
		psRunning := processmanager.IsRunning(processmanager.PEER_SEARCHER)
		if psPolicy && !psRunning {
			processmanager.Spawn("SERVICE_BIN", "PeerSearcher")
		} else if !psPolicy && psRunning {
			logger.Log("Peer searcher process end.")
			processmanager.Kill(processmanager.PEER_SEARCHER)
		}
	}
}

// checkDB ν•¨μˆ˜λŠ” λ°μ΄ν„°λ² μ΄μŠ€ μƒνƒœλ₯Ό ν™•μΈν•©λ‹ˆλ‹€.
func checkDB() {
	if config.DatabaseEnabled {
		if !database.Instance().IsConnected() {
			stop()
		}
	}
}

// logRotation ν•¨μˆ˜λŠ” 둜그 λ‘œν…Œμ΄μ…˜μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
func logRotation() {
	logger.Backup()
	logger.CleanOldLogs()
}

// main ν•¨μˆ˜λŠ” Master μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™” ν›„ 메인 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
func main() {
	master := NewMaster()
	master.Init()
	master.Main()
}

μ½”λ“œ μ„€λͺ…

  1. Master ꡬ쑰체:

    • λ§ˆμŠ€ν„° μ„œλΉ„μŠ€λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.

    • IterateλŠ” 반볡 횟수λ₯Ό λ‚˜νƒ€λ‚΄κ³ , Socket은 μ›Ήμ†ŒμΌ“ 연결을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

  2. NewMaster ν•¨μˆ˜:

    • Master μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

    • λ§ˆμŠ€ν„° ν”„λ‘œμ„ΈμŠ€κ°€ 이미 μ‹€ν–‰ 쀑인 경우 μ€‘μ§€ν•©λ‹ˆλ‹€.

    • λ§ˆμŠ€ν„° ν”„λ‘œμ„ΈμŠ€λ₯Ό μ €μž₯ν•˜κ³ , 트래컀λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

  3. Init ν•¨μˆ˜:

    • λ§ˆμŠ€ν„° μ„œλΉ„μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³  ν•„μš”ν•œ μž‘μ—…μ„ μ„€μ •ν•©λ‹ˆλ‹€.

    • μ†ŒμΌ“ 연결을 μ„€μ •ν•˜κ³ , ν•„μš”ν•œ ν”„λ‘œμ„ΈμŠ€μ™€ λ°μ΄ν„°λ² μ΄μŠ€ μƒνƒœλ₯Ό ν™•μΈν•˜λ©°, 둜그 λ‘œν…Œμ΄μ…˜μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

  4. createSocket ν•¨μˆ˜:

    • μ›Ήμ†ŒμΌ“ 연결을 μƒμ„±ν•©λ‹ˆλ‹€.

  5. listen ν•¨μˆ˜:

    • μ†ŒμΌ“ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•˜κ³  μ²˜λ¦¬ν•©λ‹ˆλ‹€.

  6. handleMessage ν•¨μˆ˜:

    • μˆ˜μ‹ ν•œ λ©”μ‹œμ§€λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.

  7. Main ν•¨μˆ˜:

    • μ£Όμš” μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

    • μ§€μ •λœ 횟수만큼 반볡 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

  8. load ν•¨μˆ˜:

    • 초기 λ‘œλ“œ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

    • 데이터 ν’€ ν”„λ‘œμ„ΈμŠ€λ₯Ό μŠ€ν°ν•˜κ³ , μ‹€ν–‰ 쀑인지 ν™•μΈν•˜λ©°, λ‹€μ–‘ν•œ 정책을 μ„€μ •ν•©λ‹ˆλ‹€.

  9. stop ν•¨μˆ˜:

    • μ„œλΉ„μŠ€λ₯Ό μ€‘μ§€ν•©λ‹ˆλ‹€.

    • λ¦¬μ†ŒμŠ€ λ§ˆμ΄λ„ˆ, 체인 메이컀, 컬렉터, ν”Όμ–΄ μ„œμ²˜, 데이터 ν’€ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€.

  10. reload ν•¨μˆ˜:

    • μ„œλΉ„μŠ€λ₯Ό μž¬μ‹œμž‘ν•©λ‹ˆλ‹€.

    • ν˜„μž¬ 정책을 μ €μž₯ν•˜κ³ , λͺ¨λ“  ν”„λ‘œμ„ΈμŠ€λ₯Ό μ’…λ£Œν•œ ν›„ λ‹€μ‹œ λ‘œλ“œν•©λ‹ˆλ‹€.

  11. checkProcess ν•¨μˆ˜:

    • ν”„λ‘œμ„ΈμŠ€λ₯Ό ν™•μΈν•˜κ³  정책에 따라 μ‹œμž‘ λ˜λŠ” μ’…λ£Œν•©λ‹ˆλ‹€.

  12. checkDB ν•¨μˆ˜:

    • λ°μ΄ν„°λ² μ΄μŠ€ μƒνƒœλ₯Ό ν™•μΈν•˜κ³  연결이 λ˜μ–΄ μžˆμ§€ μ•ŠμœΌλ©΄ μ„œλΉ„μŠ€λ₯Ό μ€‘μ§€ν•©λ‹ˆλ‹€.

  13. logRotation ν•¨μˆ˜:

    • 둜그 λ‘œν…Œμ΄μ…˜μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

  14. main ν•¨μˆ˜:

    • Master μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™”ν•œ ν›„ 메인 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

ν•¨μˆ˜ μ„€λͺ…:

  1. NewMaster: Master μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€. Master ν”„λ‘œμ„ΈμŠ€κ°€ 이미 μ‹€ν–‰ 쀑인 경우 둜그λ₯Ό κΈ°λ‘ν•˜κ³  ν”„λ‘œκ·Έλž¨μ„ μ’…λ£Œν•©λ‹ˆλ‹€.

  2. Init: Master ν”„λ‘œμ„ΈμŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³ , μ†ŒμΌ“ λͺ…λ Ήμ–΄ λ¦¬μŠ€λ„ˆλ₯Ό μΆ”κ°€ν•˜λ©°, 루틴 μž‘μ—…μ„ μ„€μ •ν•˜κ³  μ‹€ν–‰ν•©λ‹ˆλ‹€.

  3. SafeStop: Master ν”„λ‘œμ„ΈμŠ€λ₯Ό μ•ˆμ „ν•˜κ²Œ μ€‘μ§€ν•©λ‹ˆλ‹€.

  4. SafeReload: Master ν”„λ‘œμ„ΈμŠ€λ₯Ό μ•ˆμ „ν•˜κ²Œ μž¬λ‘œλ“œν•©λ‹ˆλ‹€.

  5. Main: Master ν”„λ‘œμ„ΈμŠ€μ˜ μ£Όμš” μž‘μ—…μ„ μ‹€ν–‰ν•©λ‹ˆλ‹€.

  6. SocketOperation: μ†ŒμΌ“ μž‘μ—…μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. μˆ˜μ‹ λœ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜κ³  응닡을 λ³΄λƒ…λ‹ˆλ‹€.

  7. Load: ν•„μš”ν•œ 데이터λ₯Ό λ‘œλ“œν•˜κ³ , 각쒅 정책을 μ„€μ •ν•©λ‹ˆλ‹€.

  8. Stop: λͺ¨λ“  κ΄€λ ¨ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘μ§€ν•˜κ³  Master ν”„λ‘œμ„ΈμŠ€λ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€.

  9. Reload: Master ν”„λ‘œμ„ΈμŠ€λ₯Ό μž¬λ‘œλ“œν•©λ‹ˆλ‹€.

  10. CheckProcess: κ΄€λ ¨ ν”„λ‘œμ„ΈμŠ€μ˜ μƒνƒœλ₯Ό ν™•μΈν•˜κ³  κ΄€λ¦¬ν•©λ‹ˆλ‹€.

  11. CheckDB: λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° μƒνƒœλ₯Ό ν™•μΈν•©λ‹ˆλ‹€.

  12. LogRotation: 둜그λ₯Ό λ°±μ—…ν•˜κ³  였래된 둜그λ₯Ό μ •λ¦¬ν•©λ‹ˆλ‹€.

  13. main: Master μ„œλΉ„μŠ€μ˜ μ§„μž…μ μœΌλ‘œ, Master μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  μ΄ˆκΈ°ν™”ν•œ ν›„ μ£Όμš” μž‘μ—…μ„ μ‹€ν–‰ν•©λ‹ˆλ‹€.

Last updated