2022-03-15 18:32:39 +08:00
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package caches
import (
"database/sql"
"errors"
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
2022-08-17 21:04:00 +08:00
"github.com/TeaOSLab/EdgeNode/internal/goman"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
2022-03-15 18:32:39 +08:00
"github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
2022-09-07 13:55:36 +08:00
"github.com/iwind/TeaGo/logs"
2022-03-15 18:32:39 +08:00
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
2022-11-26 11:05:46 +08:00
"net"
"net/url"
2022-09-07 14:44:36 +08:00
"os"
2022-11-20 18:07:46 +08:00
"path/filepath"
2022-03-16 16:20:53 +08:00
"runtime"
"strings"
2022-03-15 18:32:39 +08:00
"time"
)
type FileListDB struct {
2022-08-14 15:17:07 +08:00
dbPath string
2022-03-16 16:20:53 +08:00
readDB * dbs . DB
writeDB * dbs . DB
2022-03-15 18:32:39 +08:00
2022-08-17 21:04:00 +08:00
writeBatch * dbs . Batch
2022-08-20 11:47:57 +08:00
hashMap * FileListHashMap
2022-03-15 18:32:39 +08:00
itemsTableName string
hitsTableName string
total int64
isClosed bool
isReady bool
// cacheItems
2022-08-17 21:04:00 +08:00
existsByHashStmt * dbs . Stmt // 根据hash检查是否存在
insertStmt * dbs . Stmt // 写入数据
insertSQL string
selectByHashStmt * dbs . Stmt // 使用hash查询数据
2022-08-20 11:47:57 +08:00
selectHashListStmt * dbs . Stmt
2022-08-17 21:04:00 +08:00
deleteByHashStmt * dbs . Stmt // 根据hash删除数据
deleteByHashSQL string
2022-09-07 11:34:26 +08:00
statStmt * dbs . Stmt // 统计
purgeStmt * dbs . Stmt // 清理
deleteAllStmt * dbs . Stmt // 删除所有数据
listOlderItemsStmt * dbs . Stmt // 读取较早存储的缓存
updateAccessWeekSQL string // 修改访问日期
2022-03-15 18:32:39 +08:00
// hits
2022-09-07 11:34:26 +08:00
insertHitSQL string // 写入数据
increaseHitSQL string // 增加点击量
deleteHitByHashSQL string // 根据hash删除数据
2022-03-15 18:32:39 +08:00
}
func NewFileListDB ( ) * FileListDB {
2022-08-20 11:47:57 +08:00
return & FileListDB {
hashMap : NewFileListHashMap ( ) ,
}
2022-03-15 18:32:39 +08:00
}
func ( this * FileListDB ) Open ( dbPath string ) error {
2022-08-14 15:17:07 +08:00
this . dbPath = dbPath
2022-08-20 11:47:57 +08:00
// 动态调整Cache值
var cacheSize = 32000
var memoryGB = utils . SystemMemoryGB ( )
if memoryGB >= 8 {
cacheSize += 32000 * memoryGB / 8
}
2022-03-16 16:20:53 +08:00
// write db
2022-08-20 11:47:57 +08:00
writeDB , err := sql . Open ( "sqlite3" , "file:" + dbPath + "?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size=" + types . String ( cacheSize ) + "&_secure_delete=FAST" )
2022-03-15 18:32:39 +08:00
if err != nil {
2022-03-16 16:20:53 +08:00
return errors . New ( "open write database failed: " + err . Error ( ) )
2022-03-15 18:32:39 +08:00
}
2022-03-16 16:20:53 +08:00
writeDB . SetMaxOpenConns ( 1 )
2022-03-15 18:32:39 +08:00
2022-09-07 13:55:36 +08:00
this . writeDB = dbs . NewDB ( writeDB )
2022-03-15 18:32:39 +08:00
// TODO 耗时过长,暂时不整理数据库
2022-04-20 18:23:26 +08:00
// TODO 需要根据行数来判断是否VACUUM
2022-09-07 11:34:26 +08:00
// TODO 注意VACUUM反而可能让数据库文件变大
2022-03-15 18:32:39 +08:00
/ * * _ , err = db . Exec ( "VACUUM" )
if err != nil {
return err
} * * /
2022-09-07 14:44:36 +08:00
// 检查是否损坏
// TODO 暂时屏蔽,因为用时过长
var recoverEnv , _ = os . LookupEnv ( "EdgeRecover" )
if len ( recoverEnv ) > 0 && this . shouldRecover ( ) {
for _ , indexName := range [ ] string { "staleAt" , "hash" } {
_ , _ = this . writeDB . Exec ( ` REINDEX " ` + indexName + ` " ` )
}
}
2022-08-17 21:04:00 +08:00
this . writeBatch = dbs . NewBatch ( writeDB , 4 )
this . writeBatch . OnFail ( func ( err error ) {
2022-11-20 18:07:46 +08:00
remotelogs . Warn ( "LIST_FILE_DB" , "run batch failed: " + err . Error ( ) + " (" + filepath . Base ( this . dbPath ) + ")" )
2022-08-17 21:04:00 +08:00
} )
goman . New ( func ( ) {
this . writeBatch . Exec ( )
} )
2022-03-15 18:32:39 +08:00
if teaconst . EnableDBStat {
2022-08-17 21:04:00 +08:00
this . writeBatch . EnableStat ( true )
2022-03-16 16:20:53 +08:00
this . writeDB . EnableStat ( true )
}
// read db
2022-08-20 11:47:57 +08:00
readDB , err := sql . Open ( "sqlite3" , "file:" + dbPath + "?cache=private&mode=ro&_journal_mode=WAL&_sync=OFF&_cache_size=" + types . String ( cacheSize ) )
2022-03-16 16:20:53 +08:00
if err != nil {
return errors . New ( "open read database failed: " + err . Error ( ) )
}
readDB . SetMaxOpenConns ( runtime . NumCPU ( ) )
this . readDB = dbs . NewDB ( readDB )
if teaconst . EnableDBStat {
this . readDB . EnableStat ( true )
2022-03-15 18:32:39 +08:00
}
return nil
}
func ( this * FileListDB ) Init ( ) error {
this . itemsTableName = "cacheItems"
this . hitsTableName = "hits"
// 创建
var err = this . initTables ( 1 )
if err != nil {
return errors . New ( "init tables failed: " + err . Error ( ) )
}
// 读取总数量
2022-03-16 16:20:53 +08:00
row := this . readDB . QueryRow ( ` SELECT COUNT(*) FROM " ` + this . itemsTableName + ` " ` )
2022-03-15 18:32:39 +08:00
if row . Err ( ) != nil {
return row . Err ( )
}
var total int64
err = row . Scan ( & total )
if err != nil {
return err
}
this . total = total
// 常用语句
2022-04-20 18:23:26 +08:00
this . existsByHashStmt , err = this . readDB . Prepare ( ` SELECT "expiredAt" FROM " ` + this . itemsTableName + ` " INDEXED BY "hash" WHERE "hash"=? AND expiredAt>? LIMIT 1 ` )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-09-07 11:34:26 +08:00
this . insertSQL = ` INSERT INTO " ` + this . itemsTableName + ` " ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt", "accessWeek") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `
2022-08-17 21:04:00 +08:00
this . insertStmt , err = this . writeDB . Prepare ( this . insertSQL )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-03-16 16:20:53 +08:00
this . selectByHashStmt , err = this . readDB . Prepare ( ` SELECT "key", "headerSize", "bodySize", "metaSize", "expiredAt" FROM " ` + this . itemsTableName + ` " WHERE "hash"=? LIMIT 1 ` )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-08-20 11:47:57 +08:00
this . selectHashListStmt , err = this . readDB . Prepare ( ` SELECT "id", "hash" FROM " ` + this . itemsTableName + ` " WHERE id>:id ORDER BY id ASC LIMIT 2000 ` )
2022-08-17 21:04:00 +08:00
this . deleteByHashSQL = ` DELETE FROM " ` + this . itemsTableName + ` " WHERE "hash"=? `
this . deleteByHashStmt , err = this . writeDB . Prepare ( this . deleteByHashSQL )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-03-16 16:20:53 +08:00
this . statStmt , err = this . readDB . Prepare ( ` SELECT COUNT(*), IFNULL(SUM(headerSize+bodySize+metaSize), 0), IFNULL(SUM(headerSize+bodySize), 0) FROM " ` + this . itemsTableName + ` " ` )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-03-16 16:20:53 +08:00
this . purgeStmt , err = this . readDB . Prepare ( ` SELECT "hash" FROM " ` + this . itemsTableName + ` " WHERE staleAt<=? LIMIT ? ` )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-03-16 16:20:53 +08:00
this . deleteAllStmt , err = this . writeDB . Prepare ( ` DELETE FROM " ` + this . itemsTableName + ` " ` )
2022-03-15 18:32:39 +08:00
if err != nil {
return err
}
2022-09-07 11:34:26 +08:00
this . listOlderItemsStmt , err = this . readDB . Prepare ( ` SELECT "hash" FROM " ` + this . itemsTableName + ` " ORDER BY "accessWeek" ASC, "id" ASC LIMIT ? ` )
2022-09-18 16:18:31 +08:00
if err != nil {
return err
}
2022-09-07 11:34:26 +08:00
this . updateAccessWeekSQL = ` UPDATE " ` + this . itemsTableName + ` " SET "accessWeek"=? WHERE "hash"=? `
2022-03-15 18:32:39 +08:00
2022-08-20 11:47:57 +08:00
this . insertHitSQL = ` INSERT INTO " ` + this . hitsTableName + ` " ("hash", "week2Hits", "week") VALUES (?, 1, ?) `
2022-03-15 18:32:39 +08:00
2022-08-20 11:47:57 +08:00
this . increaseHitSQL = ` INSERT INTO " ` + this . hitsTableName + ` " ("hash", "week2Hits", "week") VALUES (?, 1, ?) ON CONFLICT("hash") DO UPDATE SET "week1Hits"=IIF("week"=?, "week1Hits", "week2Hits"), "week2Hits"=IIF("week"=?, "week2Hits"+1, 1), "week"=? `
2022-03-15 18:32:39 +08:00
2022-08-20 11:47:57 +08:00
this . deleteHitByHashSQL = ` DELETE FROM " ` + this . hitsTableName + ` " WHERE "hash"=? `
2022-03-15 18:32:39 +08:00
this . isReady = true
2022-08-20 11:47:57 +08:00
// 加载HashMap
go func ( ) {
err := this . hashMap . Load ( this )
if err != nil {
remotelogs . Error ( "LIST_FILE_DB" , "load hash map failed: " + err . Error ( ) + "(file: " + this . dbPath + ")" )
}
} ( )
2022-03-15 18:32:39 +08:00
return nil
}
func ( this * FileListDB ) IsReady ( ) bool {
return this . isReady
}
func ( this * FileListDB ) Total ( ) int64 {
return this . total
}
2022-08-17 21:04:00 +08:00
func ( this * FileListDB ) AddAsync ( hash string , item * Item ) error {
2022-08-20 11:47:57 +08:00
this . hashMap . Add ( hash )
2022-08-17 21:04:00 +08:00
if item . StaleAt == 0 {
item . StaleAt = item . ExpiredAt
}
2022-09-07 11:34:26 +08:00
this . writeBatch . Add ( this . insertSQL , hash , item . Key , item . HeaderSize , item . BodySize , item . MetaSize , item . ExpiredAt , item . StaleAt , item . Host , item . ServerId , utils . UnixTime ( ) , timeutil . Format ( "YW" ) )
2022-08-17 21:04:00 +08:00
return nil
}
func ( this * FileListDB ) AddSync ( hash string , item * Item ) error {
2022-08-20 11:47:57 +08:00
this . hashMap . Add ( hash )
2022-03-15 18:32:39 +08:00
if item . StaleAt == 0 {
item . StaleAt = item . ExpiredAt
}
2022-09-07 11:34:26 +08:00
_ , err := this . insertStmt . Exec ( hash , item . Key , item . HeaderSize , item . BodySize , item . MetaSize , item . ExpiredAt , item . StaleAt , item . Host , item . ServerId , utils . UnixTime ( ) , timeutil . Format ( "YW" ) )
2022-03-15 18:32:39 +08:00
if err != nil {
2022-08-14 15:17:07 +08:00
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
return nil
}
2022-08-17 21:04:00 +08:00
func ( this * FileListDB ) DeleteAsync ( hash string ) error {
2022-08-20 11:47:57 +08:00
this . hashMap . Delete ( hash )
2022-08-17 21:04:00 +08:00
this . writeBatch . Add ( this . deleteByHashSQL , hash )
return nil
}
func ( this * FileListDB ) DeleteSync ( hash string ) error {
2022-08-20 11:47:57 +08:00
this . hashMap . Delete ( hash )
2022-08-17 21:04:00 +08:00
_ , err := this . deleteByHashStmt . Exec ( hash )
if err != nil {
return err
}
return nil
}
2022-03-15 18:32:39 +08:00
func ( this * FileListDB ) ListExpiredItems ( count int ) ( hashList [ ] string , err error ) {
if ! this . isReady {
return nil , nil
}
if count <= 0 {
count = 100
}
rows , err := this . purgeStmt . Query ( time . Now ( ) . Unix ( ) , count )
if err != nil {
return nil , err
}
defer func ( ) {
_ = rows . Close ( )
} ( )
for rows . Next ( ) {
var hash string
err = rows . Scan ( & hash )
if err != nil {
return nil , err
}
hashList = append ( hashList , hash )
}
return hashList , nil
}
func ( this * FileListDB ) ListLFUItems ( count int ) ( hashList [ ] string , err error ) {
if ! this . isReady {
return nil , nil
}
if count <= 0 {
count = 100
}
2022-09-07 11:34:26 +08:00
// 先找过期的
hashList , err = this . ListExpiredItems ( count )
2022-03-15 18:32:39 +08:00
if err != nil {
return
}
2022-09-07 11:34:26 +08:00
var l = len ( hashList )
2022-03-15 18:32:39 +08:00
2022-09-07 13:55:36 +08:00
// 从旧缓存中补充
if l < count {
oldHashList , err := this . listOlderItems ( count - l )
if err != nil {
return nil , err
}
hashList = append ( hashList , oldHashList ... )
}
return hashList , nil
2022-03-15 18:32:39 +08:00
}
2022-08-20 11:47:57 +08:00
func ( this * FileListDB ) ListHashes ( lastId int64 ) ( hashList [ ] string , maxId int64 , err error ) {
rows , err := this . selectHashListStmt . Query ( lastId )
if err != nil {
return nil , 0 , err
}
2022-08-22 08:31:39 +08:00
var id int64
var hash string
2022-08-20 11:47:57 +08:00
for rows . Next ( ) {
err = rows . Scan ( & id , & hash )
if err != nil {
_ = rows . Close ( )
return
}
maxId = id
hashList = append ( hashList , hash )
}
2022-08-22 08:31:39 +08:00
_ = rows . Close ( )
2022-08-20 11:47:57 +08:00
return
}
func ( this * FileListDB ) IncreaseHitAsync ( hash string ) error {
2022-03-15 18:32:39 +08:00
var week = timeutil . Format ( "YW" )
2022-08-20 11:47:57 +08:00
this . writeBatch . Add ( this . increaseHitSQL , hash , week , week , week , week )
2022-09-07 11:34:26 +08:00
this . writeBatch . Add ( this . updateAccessWeekSQL , week , hash )
2022-08-20 11:47:57 +08:00
return nil
}
func ( this * FileListDB ) DeleteHitAsync ( hash string ) error {
this . writeBatch . Add ( this . deleteHitByHashSQL , hash )
return nil
2022-03-15 18:32:39 +08:00
}
func ( this * FileListDB ) CleanPrefix ( prefix string ) error {
if ! this . isReady {
return nil
}
var count = int64 ( 10000 )
var staleLife = 600 // TODO 需要可以设置
var unixTime = utils . UnixTime ( ) // 只删除当前的,不删除新的
for {
2022-03-16 16:20:53 +08:00
result , err := this . writeDB . Exec ( ` UPDATE " ` + this . itemsTableName + ` " SET expiredAt=0,staleAt=? WHERE id IN (SELECT id FROM " ` + this . itemsTableName + ` " WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)=1 LIMIT ` + types . String ( count ) + ` ) ` , unixTime + int64 ( staleLife ) , unixTime , prefix )
2022-03-15 18:32:39 +08:00
if err != nil {
2022-08-14 15:17:07 +08:00
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
affectedRows , err := result . RowsAffected ( )
if err != nil {
return err
}
if affectedRows < count {
return nil
}
}
}
2022-11-26 11:05:46 +08:00
func ( this * FileListDB ) CleanMatchKey ( key string ) error {
if ! this . isReady {
return nil
}
// 忽略 @GOEDGE_
if strings . Contains ( key , SuffixAll ) {
return nil
}
u , err := url . Parse ( key )
if err != nil {
return nil
}
var host = u . Host
hostPart , _ , err := net . SplitHostPort ( host )
if err == nil && len ( hostPart ) > 0 {
host = hostPart
}
if len ( host ) == 0 {
return nil
}
// 转义
var queryKey = strings . ReplaceAll ( key , "%" , "\\%" )
queryKey = strings . ReplaceAll ( queryKey , "_" , "\\_" )
queryKey = strings . Replace ( queryKey , "*" , "%" , 1 )
// TODO 检查大批量数据下的操作性能
var staleLife = 600 // TODO 需要可以设置
var unixTime = utils . UnixTime ( ) // 只删除当前的,不删除新的
_ , err = this . writeDB . Exec ( ` UPDATE " ` + this . itemsTableName + ` " SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\' ` , unixTime + int64 ( staleLife ) , host , "*." + host , queryKey )
if err != nil {
return err
}
_ , err = this . writeDB . Exec ( ` UPDATE " ` + this . itemsTableName + ` " SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\' ` , unixTime + int64 ( staleLife ) , host , "*." + host , queryKey + SuffixAll + "%" )
if err != nil {
return err
}
return nil
}
func ( this * FileListDB ) CleanMatchPrefix ( prefix string ) error {
if ! this . isReady {
return nil
}
u , err := url . Parse ( prefix )
if err != nil {
return nil
}
var host = u . Host
hostPart , _ , err := net . SplitHostPort ( host )
if err == nil && len ( hostPart ) > 0 {
host = hostPart
}
if len ( host ) == 0 {
return nil
}
// 转义
var queryPrefix = strings . ReplaceAll ( prefix , "%" , "\\%" )
queryPrefix = strings . ReplaceAll ( queryPrefix , "_" , "\\_" )
queryPrefix = strings . Replace ( queryPrefix , "*" , "%" , 1 )
queryPrefix += "%"
// TODO 检查大批量数据下的操作性能
var staleLife = 600 // TODO 需要可以设置
var unixTime = utils . UnixTime ( ) // 只删除当前的,不删除新的
_ , err = this . writeDB . Exec ( ` UPDATE " ` + this . itemsTableName + ` " SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\' ` , unixTime + int64 ( staleLife ) , host , "*." + host , queryPrefix )
return err
}
2022-03-15 18:32:39 +08:00
func ( this * FileListDB ) CleanAll ( ) error {
if ! this . isReady {
return nil
}
_ , err := this . deleteAllStmt . Exec ( )
if err != nil {
2022-08-14 15:17:07 +08:00
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
2022-08-20 11:47:57 +08:00
this . hashMap . Clean ( )
2022-03-15 18:32:39 +08:00
return nil
}
func ( this * FileListDB ) Close ( ) error {
this . isClosed = true
this . isReady = false
2022-03-16 16:20:53 +08:00
if this . existsByHashStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . existsByHashStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
if this . insertStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . insertStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
if this . selectByHashStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . selectByHashStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
2022-08-20 11:47:57 +08:00
if this . selectHashListStmt != nil {
_ = this . selectHashListStmt . Close ( )
}
2022-03-16 16:20:53 +08:00
if this . deleteByHashStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . deleteByHashStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
if this . statStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . statStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
if this . purgeStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . purgeStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
if this . deleteAllStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . deleteAllStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
if this . listOlderItemsStmt != nil {
2022-03-15 18:32:39 +08:00
_ = this . listOlderItemsStmt . Close ( )
2022-03-16 16:20:53 +08:00
}
2022-03-15 18:32:39 +08:00
2022-09-07 14:54:36 +08:00
if this . writeBatch != nil {
this . writeBatch . Close ( )
}
2022-03-16 16:20:53 +08:00
var errStrings [ ] string
if this . readDB != nil {
err := this . readDB . Close ( )
if err != nil {
errStrings = append ( errStrings , err . Error ( ) )
}
2022-03-15 18:32:39 +08:00
}
2022-03-16 16:20:53 +08:00
if this . writeDB != nil {
err := this . writeDB . Close ( )
if err != nil {
errStrings = append ( errStrings , err . Error ( ) )
}
}
if len ( errStrings ) == 0 {
return nil
}
return errors . New ( "close database failed: " + strings . Join ( errStrings , ", " ) )
2022-03-15 18:32:39 +08:00
}
2022-08-14 15:17:07 +08:00
func ( this * FileListDB ) WrapError ( err error ) error {
if err == nil {
return nil
}
return errors . New ( err . Error ( ) + "(file: " + this . dbPath + ")" )
}
2022-03-15 18:32:39 +08:00
// 初始化
func ( this * FileListDB ) initTables ( times int ) error {
{
// expiredAt - 过期时间,用来判断有无过期
2022-04-20 18:23:26 +08:00
// staleAt - 过时缓存最大时间,用来清理缓存
2022-10-23 20:45:41 +08:00
// 不对 hash 增加 unique 参数,是尽可能避免产生 malformed 错误
2022-03-16 16:20:53 +08:00
_ , err := this . writeDB . Exec ( ` CREATE TABLE IF NOT EXISTS " ` + this . itemsTableName + ` " (
2022-03-15 18:32:39 +08:00
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT ,
"hash" varchar ( 32 ) ,
"key" varchar ( 1024 ) ,
"tag" varchar ( 64 ) ,
"headerSize" integer DEFAULT 0 ,
"bodySize" integer DEFAULT 0 ,
"metaSize" integer DEFAULT 0 ,
"expiredAt" integer DEFAULT 0 ,
"staleAt" integer DEFAULT 0 ,
"createdAt" integer DEFAULT 0 ,
"host" varchar ( 128 ) ,
2022-09-07 11:34:26 +08:00
"serverId" integer ,
"accessWeek" varchar ( 6 )
2022-03-15 18:32:39 +08:00
) ;
2022-04-20 18:23:26 +08:00
DROP INDEX IF EXISTS "createdAt" ;
DROP INDEX IF EXISTS "expiredAt" ;
DROP INDEX IF EXISTS "serverId" ;
2022-03-15 18:32:39 +08:00
CREATE INDEX IF NOT EXISTS "staleAt"
ON "` + this.itemsTableName + `" (
"staleAt" ASC
) ;
2022-10-23 20:45:41 +08:00
CREATE INDEX IF NOT EXISTS "hash"
2022-03-15 18:32:39 +08:00
ON "` + this.itemsTableName + `" (
"hash" ASC
) ;
2022-09-07 11:34:26 +08:00
ALTER TABLE "cacheItems" ADD "accessWeek" varchar ( 6 ) ;
2022-03-15 18:32:39 +08:00
` )
if err != nil {
2022-09-07 11:34:26 +08:00
// 忽略可以预期的错误
if strings . Contains ( err . Error ( ) , "duplicate column name" ) {
err = nil
}
2022-03-15 18:32:39 +08:00
// 尝试删除重建
2022-09-07 11:34:26 +08:00
if err != nil {
if times < 3 {
_ , dropErr := this . writeDB . Exec ( ` DROP TABLE " ` + this . itemsTableName + ` " ` )
if dropErr == nil {
return this . initTables ( times + 1 )
}
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
2022-09-07 11:34:26 +08:00
2022-08-14 15:17:07 +08:00
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
}
}
{
2022-03-16 16:20:53 +08:00
_ , err := this . writeDB . Exec ( ` CREATE TABLE IF NOT EXISTS " ` + this . hitsTableName + ` " (
2022-03-15 18:32:39 +08:00
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT ,
"hash" varchar ( 32 ) ,
"week1Hits" integer DEFAULT 0 ,
"week2Hits" integer DEFAULT 0 ,
"week" varchar ( 6 )
) ;
CREATE UNIQUE INDEX IF NOT EXISTS "hits_hash"
ON "` + this.hitsTableName + `" (
"hash" ASC
) ;
` )
if err != nil {
// 尝试删除重建
if times < 3 {
2022-03-16 16:20:53 +08:00
_ , dropErr := this . writeDB . Exec ( ` DROP TABLE " ` + this . hitsTableName + ` " ` )
2022-03-15 18:32:39 +08:00
if dropErr == nil {
return this . initTables ( times + 1 )
}
2022-08-14 15:17:07 +08:00
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
2022-08-14 15:17:07 +08:00
return this . WrapError ( err )
2022-03-15 18:32:39 +08:00
}
}
return nil
}
func ( this * FileListDB ) listOlderItems ( count int ) ( hashList [ ] string , err error ) {
rows , err := this . listOlderItemsStmt . Query ( count )
if err != nil {
return nil , err
}
defer func ( ) {
_ = rows . Close ( )
} ( )
for rows . Next ( ) {
var hash string
err = rows . Scan ( & hash )
if err != nil {
return nil , err
}
hashList = append ( hashList , hash )
}
return hashList , nil
}
2022-09-07 13:55:36 +08:00
func ( this * FileListDB ) shouldRecover ( ) bool {
result , err := this . writeDB . Query ( "pragma integrity_check;" )
if err != nil {
logs . Println ( result )
}
var errString = ""
var shouldRecover = false
for result . Next ( ) {
err = result . Scan ( & errString )
if strings . TrimSpace ( errString ) != "ok" {
shouldRecover = true
}
break
}
_ = result . Close ( )
return shouldRecover
}