本站源代码
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

392 lines
6.7KB

  1. package nodb
  2. import (
  3. "bufio"
  4. "encoding/binary"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "path"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. "time"
  14. "github.com/lunny/log"
  15. "github.com/lunny/nodb/config"
  16. )
  17. type BinLogHead struct {
  18. CreateTime uint32
  19. BatchId uint32
  20. PayloadLen uint32
  21. }
  22. func (h *BinLogHead) Len() int {
  23. return 12
  24. }
  25. func (h *BinLogHead) Write(w io.Writer) error {
  26. if err := binary.Write(w, binary.BigEndian, h.CreateTime); err != nil {
  27. return err
  28. }
  29. if err := binary.Write(w, binary.BigEndian, h.BatchId); err != nil {
  30. return err
  31. }
  32. if err := binary.Write(w, binary.BigEndian, h.PayloadLen); err != nil {
  33. return err
  34. }
  35. return nil
  36. }
  37. func (h *BinLogHead) handleReadError(err error) error {
  38. if err == io.EOF {
  39. return io.ErrUnexpectedEOF
  40. } else {
  41. return err
  42. }
  43. }
  44. func (h *BinLogHead) Read(r io.Reader) error {
  45. var err error
  46. if err = binary.Read(r, binary.BigEndian, &h.CreateTime); err != nil {
  47. return err
  48. }
  49. if err = binary.Read(r, binary.BigEndian, &h.BatchId); err != nil {
  50. return h.handleReadError(err)
  51. }
  52. if err = binary.Read(r, binary.BigEndian, &h.PayloadLen); err != nil {
  53. return h.handleReadError(err)
  54. }
  55. return nil
  56. }
  57. func (h *BinLogHead) InSameBatch(ho *BinLogHead) bool {
  58. if h.CreateTime == ho.CreateTime && h.BatchId == ho.BatchId {
  59. return true
  60. } else {
  61. return false
  62. }
  63. }
  64. /*
  65. index file format:
  66. ledis-bin.00001
  67. ledis-bin.00002
  68. ledis-bin.00003
  69. log file format
  70. Log: Head|PayloadData
  71. Head: createTime|batchId|payloadData
  72. */
  73. type BinLog struct {
  74. sync.Mutex
  75. path string
  76. cfg *config.BinLogConfig
  77. logFile *os.File
  78. logWb *bufio.Writer
  79. indexName string
  80. logNames []string
  81. lastLogIndex int64
  82. batchId uint32
  83. ch chan struct{}
  84. }
  85. func NewBinLog(cfg *config.Config) (*BinLog, error) {
  86. l := new(BinLog)
  87. l.cfg = &cfg.BinLog
  88. l.cfg.Adjust()
  89. l.path = path.Join(cfg.DataDir, "binlog")
  90. if err := os.MkdirAll(l.path, os.ModePerm); err != nil {
  91. return nil, err
  92. }
  93. l.logNames = make([]string, 0, 16)
  94. l.ch = make(chan struct{})
  95. if err := l.loadIndex(); err != nil {
  96. return nil, err
  97. }
  98. return l, nil
  99. }
  100. func (l *BinLog) flushIndex() error {
  101. data := strings.Join(l.logNames, "\n")
  102. bakName := fmt.Sprintf("%s.bak", l.indexName)
  103. f, err := os.OpenFile(bakName, os.O_WRONLY|os.O_CREATE, 0666)
  104. if err != nil {
  105. log.Error("create binlog bak index error %s", err.Error())
  106. return err
  107. }
  108. if _, err := f.WriteString(data); err != nil {
  109. log.Error("write binlog index error %s", err.Error())
  110. f.Close()
  111. return err
  112. }
  113. f.Close()
  114. if err := os.Rename(bakName, l.indexName); err != nil {
  115. log.Error("rename binlog bak index error %s", err.Error())
  116. return err
  117. }
  118. return nil
  119. }
  120. func (l *BinLog) loadIndex() error {
  121. l.indexName = path.Join(l.path, fmt.Sprintf("ledis-bin.index"))
  122. if _, err := os.Stat(l.indexName); os.IsNotExist(err) {
  123. //no index file, nothing to do
  124. } else {
  125. indexData, err := ioutil.ReadFile(l.indexName)
  126. if err != nil {
  127. return err
  128. }
  129. lines := strings.Split(string(indexData), "\n")
  130. for _, line := range lines {
  131. line = strings.Trim(line, "\r\n ")
  132. if len(line) == 0 {
  133. continue
  134. }
  135. if _, err := os.Stat(path.Join(l.path, line)); err != nil {
  136. log.Error("load index line %s error %s", line, err.Error())
  137. return err
  138. } else {
  139. l.logNames = append(l.logNames, line)
  140. }
  141. }
  142. }
  143. if l.cfg.MaxFileNum > 0 && len(l.logNames) > l.cfg.MaxFileNum {
  144. //remove oldest logfile
  145. if err := l.Purge(len(l.logNames) - l.cfg.MaxFileNum); err != nil {
  146. return err
  147. }
  148. }
  149. var err error
  150. if len(l.logNames) == 0 {
  151. l.lastLogIndex = 1
  152. } else {
  153. lastName := l.logNames[len(l.logNames)-1]
  154. if l.lastLogIndex, err = strconv.ParseInt(path.Ext(lastName)[1:], 10, 64); err != nil {
  155. log.Error("invalid logfile name %s", err.Error())
  156. return err
  157. }
  158. //like mysql, if server restart, a new binlog will create
  159. l.lastLogIndex++
  160. }
  161. return nil
  162. }
  163. func (l *BinLog) getLogFile() string {
  164. return l.FormatLogFileName(l.lastLogIndex)
  165. }
  166. func (l *BinLog) openNewLogFile() error {
  167. var err error
  168. lastName := l.getLogFile()
  169. logPath := path.Join(l.path, lastName)
  170. if l.logFile, err = os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY, 0666); err != nil {
  171. log.Error("open new logfile error %s", err.Error())
  172. return err
  173. }
  174. if l.cfg.MaxFileNum > 0 && len(l.logNames) == l.cfg.MaxFileNum {
  175. l.purge(1)
  176. }
  177. l.logNames = append(l.logNames, lastName)
  178. if l.logWb == nil {
  179. l.logWb = bufio.NewWriterSize(l.logFile, 1024)
  180. } else {
  181. l.logWb.Reset(l.logFile)
  182. }
  183. if err = l.flushIndex(); err != nil {
  184. return err
  185. }
  186. return nil
  187. }
  188. func (l *BinLog) checkLogFileSize() bool {
  189. if l.logFile == nil {
  190. return false
  191. }
  192. st, _ := l.logFile.Stat()
  193. if st.Size() >= int64(l.cfg.MaxFileSize) {
  194. l.closeLog()
  195. return true
  196. }
  197. return false
  198. }
  199. func (l *BinLog) closeLog() {
  200. l.lastLogIndex++
  201. l.logFile.Close()
  202. l.logFile = nil
  203. }
  204. func (l *BinLog) purge(n int) {
  205. for i := 0; i < n; i++ {
  206. logPath := path.Join(l.path, l.logNames[i])
  207. os.Remove(logPath)
  208. }
  209. copy(l.logNames[0:], l.logNames[n:])
  210. l.logNames = l.logNames[0 : len(l.logNames)-n]
  211. }
  212. func (l *BinLog) Close() {
  213. if l.logFile != nil {
  214. l.logFile.Close()
  215. l.logFile = nil
  216. }
  217. }
  218. func (l *BinLog) LogNames() []string {
  219. return l.logNames
  220. }
  221. func (l *BinLog) LogFileName() string {
  222. return l.getLogFile()
  223. }
  224. func (l *BinLog) LogFilePos() int64 {
  225. if l.logFile == nil {
  226. return 0
  227. } else {
  228. st, _ := l.logFile.Stat()
  229. return st.Size()
  230. }
  231. }
  232. func (l *BinLog) LogFileIndex() int64 {
  233. return l.lastLogIndex
  234. }
  235. func (l *BinLog) FormatLogFileName(index int64) string {
  236. return fmt.Sprintf("ledis-bin.%07d", index)
  237. }
  238. func (l *BinLog) FormatLogFilePath(index int64) string {
  239. return path.Join(l.path, l.FormatLogFileName(index))
  240. }
  241. func (l *BinLog) LogPath() string {
  242. return l.path
  243. }
  244. func (l *BinLog) Purge(n int) error {
  245. l.Lock()
  246. defer l.Unlock()
  247. if len(l.logNames) == 0 {
  248. return nil
  249. }
  250. if n >= len(l.logNames) {
  251. n = len(l.logNames)
  252. //can not purge current log file
  253. if l.logNames[n-1] == l.getLogFile() {
  254. n = n - 1
  255. }
  256. }
  257. l.purge(n)
  258. return l.flushIndex()
  259. }
  260. func (l *BinLog) PurgeAll() error {
  261. l.Lock()
  262. defer l.Unlock()
  263. l.closeLog()
  264. return l.openNewLogFile()
  265. }
  266. func (l *BinLog) Log(args ...[]byte) error {
  267. l.Lock()
  268. defer l.Unlock()
  269. var err error
  270. if l.logFile == nil {
  271. if err = l.openNewLogFile(); err != nil {
  272. return err
  273. }
  274. }
  275. head := &BinLogHead{}
  276. head.CreateTime = uint32(time.Now().Unix())
  277. head.BatchId = l.batchId
  278. l.batchId++
  279. for _, data := range args {
  280. head.PayloadLen = uint32(len(data))
  281. if err := head.Write(l.logWb); err != nil {
  282. return err
  283. }
  284. if _, err := l.logWb.Write(data); err != nil {
  285. return err
  286. }
  287. }
  288. if err = l.logWb.Flush(); err != nil {
  289. log.Error("write log error %s", err.Error())
  290. return err
  291. }
  292. l.checkLogFileSize()
  293. close(l.ch)
  294. l.ch = make(chan struct{})
  295. return nil
  296. }
  297. func (l *BinLog) Wait() <-chan struct{} {
  298. return l.ch
  299. }
上海开阖软件有限公司 沪ICP备12045867号-1