You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

522 lines
15 KiB

5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
5 years ago
  1. package shell
  2. import (
  3. "bufio"
  4. "context"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "math"
  10. "os"
  11. "path/filepath"
  12. "sync"
  13. "github.com/chrislusf/seaweedfs/weed/filer"
  14. "github.com/chrislusf/seaweedfs/weed/operation"
  15. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  16. "github.com/chrislusf/seaweedfs/weed/pb/master_pb"
  17. "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb"
  18. "github.com/chrislusf/seaweedfs/weed/storage/needle_map"
  19. "github.com/chrislusf/seaweedfs/weed/storage/types"
  20. "github.com/chrislusf/seaweedfs/weed/util"
  21. )
  22. func init() {
  23. Commands = append(Commands, &commandVolumeFsck{})
  24. }
  25. type commandVolumeFsck struct {
  26. env *CommandEnv
  27. }
  28. func (c *commandVolumeFsck) Name() string {
  29. return "volume.fsck"
  30. }
  31. func (c *commandVolumeFsck) Help() string {
  32. return `check all volumes to find entries not used by the filer
  33. Important assumption!!!
  34. the system is all used by one filer.
  35. This command works this way:
  36. 1. collect all file ids from all volumes, as set A
  37. 2. collect all file ids from the filer, as set B
  38. 3. find out the set A subtract B
  39. If -findMissingChunksInFiler is enabled, this works
  40. in a reverse way:
  41. 1. collect all file ids from all volumes, as set A
  42. 2. collect all file ids from the filer, as set B
  43. 3. find out the set B subtract A
  44. `
  45. }
  46. func (c *commandVolumeFsck) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
  47. if err = commandEnv.confirmIsLocked(); err != nil {
  48. return
  49. }
  50. fsckCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
  51. verbose := fsckCommand.Bool("v", false, "verbose mode")
  52. findMissingChunksInFiler := fsckCommand.Bool("findMissingChunksInFiler", false, "see help volume.fsck")
  53. applyPurging := fsckCommand.Bool("reallyDeleteFromVolume", false, "<expert only> delete data not referenced by the filer")
  54. if err = fsckCommand.Parse(args); err != nil {
  55. return nil
  56. }
  57. c.env = commandEnv
  58. // create a temp folder
  59. tempFolder, err := ioutil.TempDir("", "sw_fsck")
  60. if err != nil {
  61. return fmt.Errorf("failed to create temp folder: %v", err)
  62. }
  63. if *verbose {
  64. fmt.Fprintf(writer, "working directory: %s\n", tempFolder)
  65. }
  66. defer os.RemoveAll(tempFolder)
  67. // collect all volume id locations
  68. volumeIdToVInfo, err := c.collectVolumeIds(commandEnv, *verbose, writer)
  69. if err != nil {
  70. return fmt.Errorf("failed to collect all volume locations: %v", err)
  71. }
  72. // collect each volume file ids
  73. for volumeId, vinfo := range volumeIdToVInfo {
  74. err = c.collectOneVolumeFileIds(tempFolder, volumeId, vinfo, *verbose, writer)
  75. if err != nil {
  76. return fmt.Errorf("failed to collect file ids from volume %d on %s: %v", volumeId, vinfo.server, err)
  77. }
  78. }
  79. if *findMissingChunksInFiler {
  80. // collect all filer file ids and paths
  81. if err = c.collectFilerFileIdAndPaths(volumeIdToVInfo, tempFolder, writer, *verbose, applyPurging); err != nil {
  82. return fmt.Errorf("collectFilerFileIdAndPaths: %v", err)
  83. }
  84. // for each volume, check filer file ids
  85. if err = c.findFilerChunksMissingInVolumeServers(volumeIdToVInfo, tempFolder, writer, *verbose, applyPurging); err != nil {
  86. return fmt.Errorf("findExtraChunksInVolumeServers: %v", err)
  87. }
  88. } else {
  89. // collect all filer file ids
  90. if err = c.collectFilerFileIds(tempFolder, volumeIdToVInfo, *verbose, writer); err != nil {
  91. return fmt.Errorf("failed to collect file ids from filer: %v", err)
  92. }
  93. // volume file ids substract filer file ids
  94. if err = c.findExtraChunksInVolumeServers(volumeIdToVInfo, tempFolder, writer, *verbose, applyPurging); err != nil {
  95. return fmt.Errorf("findExtraChunksInVolumeServers: %v", err)
  96. }
  97. }
  98. return nil
  99. }
  100. func (c *commandVolumeFsck) collectFilerFileIdAndPaths(volumeIdToServer map[uint32]VInfo, tempFolder string, writer io.Writer, verbose bool, applyPurging *bool) error {
  101. if verbose {
  102. fmt.Fprintf(writer, "checking each file from filer ...\n")
  103. }
  104. files := make(map[uint32]*os.File)
  105. for vid := range volumeIdToServer {
  106. dst, openErr := os.OpenFile(getFilerFileIdFile(tempFolder, vid), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  107. if openErr != nil {
  108. return fmt.Errorf("failed to create file %s: %v", getFilerFileIdFile(tempFolder, vid), openErr)
  109. }
  110. files[vid] = dst
  111. }
  112. defer func() {
  113. for _, f := range files {
  114. f.Close()
  115. }
  116. }()
  117. type Item struct {
  118. vid uint32
  119. fileKey uint64
  120. cookie uint32
  121. path util.FullPath
  122. }
  123. return doTraverseBfsAndSaving(c.env, nil, "/", false, func(outputChan chan interface{}) {
  124. buffer := make([]byte, 8)
  125. for item := range outputChan {
  126. i := item.(*Item)
  127. if f, ok := files[i.vid]; ok {
  128. util.Uint64toBytes(buffer, i.fileKey)
  129. f.Write(buffer)
  130. util.Uint32toBytes(buffer, i.cookie)
  131. util.Uint32toBytes(buffer[4:], uint32(len(i.path)))
  132. f.Write(buffer)
  133. f.Write([]byte(i.path))
  134. } else {
  135. fmt.Fprintf(writer, "%d,%x%08x %s volume not found\n", i.vid, i.fileKey, i.cookie, i.path)
  136. }
  137. }
  138. }, func(entry *filer_pb.FullEntry, outputChan chan interface{}) (err error) {
  139. dChunks, mChunks, resolveErr := filer.ResolveChunkManifest(filer.LookupFn(c.env), entry.Entry.Chunks)
  140. if resolveErr != nil {
  141. return nil
  142. }
  143. dChunks = append(dChunks, mChunks...)
  144. for _, chunk := range dChunks {
  145. outputChan <- &Item{
  146. vid: chunk.Fid.VolumeId,
  147. fileKey: chunk.Fid.FileKey,
  148. cookie: chunk.Fid.Cookie,
  149. path: util.NewFullPath(entry.Dir, entry.Entry.Name),
  150. }
  151. }
  152. return nil
  153. })
  154. return nil
  155. }
  156. func (c *commandVolumeFsck) findFilerChunksMissingInVolumeServers(volumeIdToVInfo map[uint32]VInfo, tempFolder string, writer io.Writer, verbose bool, applyPurging *bool) error {
  157. for volumeId, vinfo := range volumeIdToVInfo {
  158. checkErr := c.oneVolumeFileIdsCheckOneVolume(tempFolder, volumeId, writer, verbose)
  159. if checkErr != nil {
  160. return fmt.Errorf("failed to collect file ids from volume %d on %s: %v", volumeId, vinfo.server, checkErr)
  161. }
  162. }
  163. return nil
  164. }
  165. func (c *commandVolumeFsck) findExtraChunksInVolumeServers(volumeIdToVInfo map[uint32]VInfo, tempFolder string, writer io.Writer, verbose bool, applyPurging *bool) error {
  166. var totalInUseCount, totalOrphanChunkCount, totalOrphanDataSize uint64
  167. for volumeId, vinfo := range volumeIdToVInfo {
  168. inUseCount, orphanFileIds, orphanDataSize, checkErr := c.oneVolumeFileIdsSubtractFilerFileIds(tempFolder, volumeId, writer, verbose)
  169. if checkErr != nil {
  170. return fmt.Errorf("failed to collect file ids from volume %d on %s: %v", volumeId, vinfo.server, checkErr)
  171. }
  172. totalInUseCount += inUseCount
  173. totalOrphanChunkCount += uint64(len(orphanFileIds))
  174. totalOrphanDataSize += orphanDataSize
  175. if verbose {
  176. for _, fid := range orphanFileIds {
  177. fmt.Fprintf(writer, "%sxxxxxxxx\n", fid)
  178. }
  179. }
  180. if *applyPurging && len(orphanFileIds) > 0 {
  181. if vinfo.isEcVolume {
  182. fmt.Fprintf(writer, "Skip purging for Erasure Coded volumes.\n")
  183. }
  184. if err := c.purgeFileIdsForOneVolume(volumeId, orphanFileIds, writer); err != nil {
  185. return fmt.Errorf("purge for volume %d: %v\n", volumeId, err)
  186. }
  187. }
  188. }
  189. if totalOrphanChunkCount == 0 {
  190. fmt.Fprintf(writer, "no orphan data\n")
  191. return nil
  192. }
  193. if !*applyPurging {
  194. pct := float64(totalOrphanChunkCount*100) / (float64(totalOrphanChunkCount + totalInUseCount))
  195. fmt.Fprintf(writer, "\nTotal\t\tentries:%d\torphan:%d\t%.2f%%\t%dB\n",
  196. totalOrphanChunkCount+totalInUseCount, totalOrphanChunkCount, pct, totalOrphanDataSize)
  197. fmt.Fprintf(writer, "This could be normal if multiple filers or no filers are used.\n")
  198. }
  199. return nil
  200. }
  201. func (c *commandVolumeFsck) collectOneVolumeFileIds(tempFolder string, volumeId uint32, vinfo VInfo, verbose bool, writer io.Writer) error {
  202. if verbose {
  203. fmt.Fprintf(writer, "collecting volume %d file ids from %s ...\n", volumeId, vinfo.server)
  204. }
  205. return operation.WithVolumeServerClient(vinfo.server, c.env.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error {
  206. ext := ".idx"
  207. if vinfo.isEcVolume {
  208. ext = ".ecx"
  209. }
  210. copyFileClient, err := volumeServerClient.CopyFile(context.Background(), &volume_server_pb.CopyFileRequest{
  211. VolumeId: volumeId,
  212. Ext: ext,
  213. CompactionRevision: math.MaxUint32,
  214. StopOffset: math.MaxInt64,
  215. Collection: vinfo.collection,
  216. IsEcVolume: vinfo.isEcVolume,
  217. IgnoreSourceFileNotFound: false,
  218. })
  219. if err != nil {
  220. return fmt.Errorf("failed to start copying volume %d%s: %v", volumeId, ext, err)
  221. }
  222. err = writeToFile(copyFileClient, getVolumeFileIdFile(tempFolder, volumeId))
  223. if err != nil {
  224. return fmt.Errorf("failed to copy %d%s from %s: %v", volumeId, ext, vinfo.server, err)
  225. }
  226. return nil
  227. })
  228. }
  229. func (c *commandVolumeFsck) collectFilerFileIds(tempFolder string, volumeIdToServer map[uint32]VInfo, verbose bool, writer io.Writer) error {
  230. if verbose {
  231. fmt.Fprintf(writer, "collecting file ids from filer ...\n")
  232. }
  233. files := make(map[uint32]*os.File)
  234. for vid := range volumeIdToServer {
  235. dst, openErr := os.OpenFile(getFilerFileIdFile(tempFolder, vid), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  236. if openErr != nil {
  237. return fmt.Errorf("failed to create file %s: %v", getFilerFileIdFile(tempFolder, vid), openErr)
  238. }
  239. files[vid] = dst
  240. }
  241. defer func() {
  242. for _, f := range files {
  243. f.Close()
  244. }
  245. }()
  246. type Item struct {
  247. vid uint32
  248. fileKey uint64
  249. }
  250. return doTraverseBfsAndSaving(c.env, nil, "/", false, func(outputChan chan interface{}) {
  251. buffer := make([]byte, 8)
  252. for item := range outputChan {
  253. i := item.(*Item)
  254. util.Uint64toBytes(buffer, i.fileKey)
  255. files[i.vid].Write(buffer)
  256. }
  257. }, func(entry *filer_pb.FullEntry, outputChan chan interface{}) (err error) {
  258. dChunks, mChunks, resolveErr := filer.ResolveChunkManifest(filer.LookupFn(c.env), entry.Entry.Chunks)
  259. if resolveErr != nil {
  260. return nil
  261. }
  262. dChunks = append(dChunks, mChunks...)
  263. for _, chunk := range dChunks {
  264. outputChan <- &Item{
  265. vid: chunk.Fid.VolumeId,
  266. fileKey: chunk.Fid.FileKey,
  267. }
  268. }
  269. return nil
  270. })
  271. }
  272. func (c *commandVolumeFsck) oneVolumeFileIdsCheckOneVolume(tempFolder string, volumeId uint32, writer io.Writer, verbose bool) (err error) {
  273. db := needle_map.NewMemDb()
  274. defer db.Close()
  275. if err = db.LoadFromIdx(getVolumeFileIdFile(tempFolder, volumeId)); err != nil {
  276. return
  277. }
  278. file := getFilerFileIdFile(tempFolder, volumeId)
  279. fp, err := os.Open(file)
  280. if err != nil {
  281. return
  282. }
  283. defer fp.Close()
  284. type Item struct {
  285. fileKey uint64
  286. cookie uint32
  287. path util.FullPath
  288. }
  289. br := bufio.NewReader(fp)
  290. buffer := make([]byte, 16)
  291. item := &Item{}
  292. var readSize int
  293. for {
  294. readSize, err = br.Read(buffer)
  295. if err != nil || readSize != 16 {
  296. if err == io.EOF {
  297. return nil
  298. } else {
  299. break
  300. }
  301. }
  302. item.fileKey = util.BytesToUint64(buffer[:8])
  303. item.cookie = util.BytesToUint32(buffer[8:12])
  304. pathSize := util.BytesToUint32(buffer[12:16])
  305. pathBytes := make([]byte, int(pathSize))
  306. _, err = br.Read(pathBytes)
  307. item.path = util.FullPath(string(pathBytes))
  308. if _, found := db.Get(types.NeedleId(item.fileKey)); !found {
  309. fmt.Fprintf(writer, "%d,%x%08x in %s not found\n", volumeId, item.fileKey, item.cookie, item.path)
  310. }
  311. }
  312. return
  313. }
  314. func (c *commandVolumeFsck) oneVolumeFileIdsSubtractFilerFileIds(tempFolder string, volumeId uint32, writer io.Writer, verbose bool) (inUseCount uint64, orphanFileIds []string, orphanDataSize uint64, err error) {
  315. db := needle_map.NewMemDb()
  316. defer db.Close()
  317. if err = db.LoadFromIdx(getVolumeFileIdFile(tempFolder, volumeId)); err != nil {
  318. return
  319. }
  320. filerFileIdsData, err := ioutil.ReadFile(getFilerFileIdFile(tempFolder, volumeId))
  321. if err != nil {
  322. return
  323. }
  324. dataLen := len(filerFileIdsData)
  325. if dataLen%8 != 0 {
  326. return 0, nil, 0, fmt.Errorf("filer data is corrupted")
  327. }
  328. for i := 0; i < len(filerFileIdsData); i += 8 {
  329. fileKey := util.BytesToUint64(filerFileIdsData[i : i+8])
  330. db.Delete(types.NeedleId(fileKey))
  331. inUseCount++
  332. }
  333. var orphanFileCount uint64
  334. db.AscendingVisit(func(n needle_map.NeedleValue) error {
  335. // fmt.Printf("%d,%x\n", volumeId, n.Key)
  336. orphanFileIds = append(orphanFileIds, fmt.Sprintf("%d,%s", volumeId, n.Key.String()))
  337. orphanFileCount++
  338. orphanDataSize += uint64(n.Size)
  339. return nil
  340. })
  341. if orphanFileCount > 0 {
  342. pct := float64(orphanFileCount*100) / (float64(orphanFileCount + inUseCount))
  343. fmt.Fprintf(writer, "volume:%d\tentries:%d\torphan:%d\t%.2f%%\t%dB\n",
  344. volumeId, orphanFileCount+inUseCount, orphanFileCount, pct, orphanDataSize)
  345. }
  346. return
  347. }
  348. type VInfo struct {
  349. server string
  350. collection string
  351. isEcVolume bool
  352. }
  353. func (c *commandVolumeFsck) collectVolumeIds(commandEnv *CommandEnv, verbose bool, writer io.Writer) (volumeIdToServer map[uint32]VInfo, err error) {
  354. if verbose {
  355. fmt.Fprintf(writer, "collecting volume id and locations from master ...\n")
  356. }
  357. volumeIdToServer = make(map[uint32]VInfo)
  358. // collect topology information
  359. topologyInfo, _, err := collectTopologyInfo(commandEnv)
  360. if err != nil {
  361. return
  362. }
  363. eachDataNode(topologyInfo, func(dc string, rack RackId, t *master_pb.DataNodeInfo) {
  364. for _, diskInfo := range t.DiskInfos {
  365. for _, vi := range diskInfo.VolumeInfos {
  366. volumeIdToServer[vi.Id] = VInfo{
  367. server: t.Id,
  368. collection: vi.Collection,
  369. isEcVolume: false,
  370. }
  371. }
  372. for _, ecShardInfo := range diskInfo.EcShardInfos {
  373. volumeIdToServer[ecShardInfo.Id] = VInfo{
  374. server: t.Id,
  375. collection: ecShardInfo.Collection,
  376. isEcVolume: true,
  377. }
  378. }
  379. }
  380. })
  381. if verbose {
  382. fmt.Fprintf(writer, "collected %d volumes and locations.\n", len(volumeIdToServer))
  383. }
  384. return
  385. }
  386. func (c *commandVolumeFsck) purgeFileIdsForOneVolume(volumeId uint32, fileIds []string, writer io.Writer) (err error) {
  387. fmt.Fprintf(writer, "purging orphan data for volume %d...\n", volumeId)
  388. locations, found := c.env.MasterClient.GetLocations(volumeId)
  389. if !found {
  390. return fmt.Errorf("failed to find volume %d locations", volumeId)
  391. }
  392. resultChan := make(chan []*volume_server_pb.DeleteResult, len(locations))
  393. var wg sync.WaitGroup
  394. for _, location := range locations {
  395. wg.Add(1)
  396. go func(server string, fidList []string) {
  397. defer wg.Done()
  398. if deleteResults, deleteErr := operation.DeleteFilesAtOneVolumeServer(server, c.env.option.GrpcDialOption, fidList, false); deleteErr != nil {
  399. err = deleteErr
  400. } else if deleteResults != nil {
  401. resultChan <- deleteResults
  402. }
  403. }(location.Url, fileIds)
  404. }
  405. wg.Wait()
  406. close(resultChan)
  407. for results := range resultChan {
  408. for _, result := range results {
  409. if result.Error != "" {
  410. fmt.Fprintf(writer, "purge error: %s\n", result.Error)
  411. }
  412. }
  413. }
  414. return
  415. }
  416. func getVolumeFileIdFile(tempFolder string, vid uint32) string {
  417. return filepath.Join(tempFolder, fmt.Sprintf("%d.idx", vid))
  418. }
  419. func getFilerFileIdFile(tempFolder string, vid uint32) string {
  420. return filepath.Join(tempFolder, fmt.Sprintf("%d.fid", vid))
  421. }
  422. func writeToFile(client volume_server_pb.VolumeServer_CopyFileClient, fileName string) error {
  423. flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
  424. dst, err := os.OpenFile(fileName, flags, 0644)
  425. if err != nil {
  426. return nil
  427. }
  428. defer dst.Close()
  429. for {
  430. resp, receiveErr := client.Recv()
  431. if receiveErr == io.EOF {
  432. break
  433. }
  434. if receiveErr != nil {
  435. return fmt.Errorf("receiving %s: %v", fileName, receiveErr)
  436. }
  437. dst.Write(resp.FileContent)
  438. }
  439. return nil
  440. }