package volume

import (
    "os"
    "path/filepath"
    "strconv"
    "sync"
    "time"
    "encoding/binary"
    "errors"
)

var (
    TruncateSize uint64 = 1 << 30 //1GB
    MaxVolumeSize uint64 = 512 * TruncateSize //512GB
)

type Volume struct {
    Id        uint64
    WriteAble bool
    index     Index
    status    *Status
    dataFile  *os.File
    mutex     sync.Mutex
}
//创建一个卷 或者磁盘空间   
func NewVolume(dir string, vid uint64) (v *Volume, err error) {
    path := filepath.Join(dir, strconv.FormatUint(vid, 10) + ".data")
    v = new(Volume)
    v.Id = vid
    v.dataFile, err = os.OpenFile(path, os.O_CREATE | os.O_RDWR, 0666)
    if err != nil {
        if os.IsPermission(err) {//判断是不是权限错误
            v.dataFile, err = os.OpenFile(path, os.O_RDONLY, 0)
            if err != nil {
                return nil, err
            }
            v.WriteAble = false
        } else {
            return nil, err
        }
    }
    v.WriteAble = true

    v.index, err = NewLevelDBIndex(dir, vid)
    if err != nil {
        return nil, err
    }

    v.status, err = NewStatus(dir, vid)
    if err != nil {
        return nil, err
    }

    return v, nil
}
//实现index接口
//
func (v *Volume)Get(fid uint64) (*File, error) {
    fi, err := v.index.Get(fid)
    if err == nil {
        return &File{DataFile: v.dataFile, Info: fi}, nil
    } else {
        return nil, err
    }
}
//删除文件
func (v *Volume)Delete(fid uint64, fileName string) error {
    v.mutex.Lock()
    defer v.mutex.Unlock()
    file, err := v.Get(fid)
    if err != nil {
        return err
    } else if file.Info.FileName != fileName {
        return errors.New("filename is wrong")
    }
    //因为文件内容前后都写入fid(8 byte) 要一起释放
    err = v.status.freeSpace(file.Info.Offset - 8, file.Info.Size + 16)
    if err != nil {
        return err
    }
    err = v.index.Delete(fid)
    return err
}
//创建文件
func (v *Volume)NewFile(fid uint64, fileName string, size uint64) (f *File, err error) {
    v.mutex.Lock()
    defer v.mutex.Unlock()
    if v.index.Has(fid) {
        return nil, errors.New("fid is existed")
    }
    offset, err := v.newSpace(size + 16)
    if err != nil {
        return nil, err
    }
    defer func() {
        if err != nil {
            if e := v.status.freeSpace(offset, size + 16); e != nil {
                panic(e)
            }
        }
    }()
    //在文件内容前后都写入fid
    b := make([]byte, 8)
    binary.BigEndian.PutUint64(b, fid)
    _, err = v.dataFile.WriteAt(b, int64(offset))
    if err != nil {
        return nil, err
    }
    _, err = v.dataFile.WriteAt(b, int64(offset + 8 + size))
    if err != nil {
        return nil, err
    }
    fileInfo := &FileInfo{
        Fid: fid,
        Offset: offset + 8,
        Size: size,
        Ctime: time.Now(),
        Mtime: time.Now(),
        Atime: time.Now(),
        FileName: fileName,
    }
    err = v.index.Set(fileInfo)
    if err != nil {
        return nil, err
    } else {
        return &File{DataFile: v.dataFile, Info: fileInfo}, nil
    }
}
//清空文件空间
func (v *Volume)truncate() {
    currentDatafileSize := v.GetDatafileSize()
    if currentDatafileSize >= MaxVolumeSize {
        return
    }
       //清空指定偏移量位置的数据
    err := v.dataFile.Truncate(int64(currentDatafileSize) + int64(TruncateSize))
    if err != nil {
        panic(err)
    }
       //释放文件空间
    err = v.status.freeSpace(currentDatafileSize, TruncateSize)
    if err != nil {
        panic(err)
    }
}
//分配新的文件空间
func (v *Volume)newSpace(size uint64) (uint64, error) {
    offset, err := v.status.newSpace(size)
    if err == nil {
        return offset, err
    }
    v.truncate()
    return v.status.newSpace(size)
}
//资源释放
func (v *Volume)Close() {
    v.mutex.Lock()
    //v.status.spaceMutex.Lock()
    //因为要退出,所以不解锁,禁止读写
    //defer v.rwMutex.Unlock()

    //将所有资源释放
    v.dataFile.Close()
    v.dataFile = nil

    v.status.db.Close()
    v.status = nil

    v.index.Close()
    v.index = nil
}
//获取数据文件大小
func (v *Volume)GetDatafileSize() uint64 {
    fi, err := v.dataFile.Stat()
    if err != nil {
        panic(err)
    }
    return uint64(fi.Size())
}
//获取最大释放文件空间
func (v *Volume)GetMaxFreeSpace() uint64 {
    currentDatafileSize := v.GetDatafileSize()
    //以块方式存储,所以自由空间无法直接相加
    maxFreeSpace := v.status.getMaxFreeSpace()
    if currentDatafileSize < MaxVolumeSize {
        freeSpace := MaxVolumeSize - currentDatafileSize
        if freeSpace > maxFreeSpace {
            maxFreeSpace = freeSpace
        }
    }
    //fid前后各占8个字节
    if maxFreeSpace > 16 {
        return maxFreeSpace - 16
    } else {
        return 0
    }
}

volume.go的更多相关文章

  1. Java中实现SAX解析xml文件到MySQL数据库

    大致步骤: 1.Java bean 2.DBHelper.java 3.重写DefaultHandler中的方法:MyHander.java 4.循环写数据库:SAXParserDemo.java ① ...

  2. CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探

    CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码 ...

  3. 理解Docker(8):Docker 存储之卷(Volume)

    (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 (4)Docker 容器的隔离性 - 使用 ...

  4. Docker Volume 之权限管理(转)

    Volume数据卷是Docker的一个重要概念.数据卷是可供一个或多个容器使用的特殊目录,可以为容器应用存储提供有价值的特性: 持久化数据与容器的生命周期解耦:在容器删除之后数据卷中的内容可以保持.D ...

  5. NFS Volume Provider(Part III) - 每天5分钟玩转 OpenStack(64)

    今天我们将前一小节创建的 NFS volume “nfs-vol-1” attach 到 instance “c2”上. 这里我们重点关注 nova-compute 如何将“nfs-vol-1” at ...

  6. NFS Volume Provider(Part II) - 每天5分钟玩转 OpenStack(63)

    上一节我们将 NFS volume provider 配置就绪,本节将创建 volume. 创建 volume 创建 NFS volume 操作方法与 LVM volume 一样,唯一区别是在 vol ...

  7. NFS Volume Provider(Part I) - 每天5分钟玩转 OpenStack(62)

    cinder-volume 支持多种 volume provider,前面我们一直使用的是默认的 LVM,本节我们将增加 NFS volume provider. 虽然 NFS 更多地应用在实验或小规 ...

  8. Boot from Volume - 每天5分钟玩转 OpenStack(61)

    Volume 除了可以用作 instance 的数据盘,也可以作为启动盘(Bootable Volume),那么如何使 volume 成为 bootable 呢? 现在我们打开 instance 的 ...

  9. Restore Volume 操作 - 每天5分钟玩转 OpenStack(60)

    前面我们 backup 了 voluem,今天我们将讨论如何 restore volume. restore 的过程其实很简单,两步走: 在存储节点上创建一个空白 volume. 将 backup 的 ...

  10. Backup Volume 操作 - 每天5分钟玩转 OpenStack(59)

    本节我们讨论 volume 的 Backup 操作. Backup 是将 volume 备份到别的地方(备份设备),将来可以通过 restore 操作恢复. Backup VS Snapshot 初看 ...

随机推荐

  1. C语言中如何写一个简单可移植而又足够随机的随机数生成器

    在C语言中标准库中的随机数产生函数的返回可能不是最优的,因为有些随机数生成器的低位并不随机,而另一些返回随机数的函数实现上又太复杂鸟.所以rand()%N并不是一个好方法,牛人给出的建议是使用: ra ...

  2. 快递单号查询免费api接口(PHP示例)

    快递单号查询API,可以对接顺丰快递查询,邮政快递查询,中通快递查询等.这些快递物流企业,提供了快递单号自动识别接口,快递单号查询接口等快递物流服务.对于电商企业,ERP服务企业,集成此接口到自己的软 ...

  3. 集群中几种session同步解决方案的比较[转]

    集群中session安全和同步是个最大的问题,下面是我收集到的几种session同步的方案,希望能通过分析其各自的优劣找出其适应的场景. 1. 客户端cookie加密 这是我以前采用的方式,简单,高效 ...

  4. [转]web服务器压力测试工具

    http_load学习心得: 测试网站每秒所能承受的平均访问量(吞吐量) http_load -parallel 5 -fetches 1000 urls.txt这段命令行是同时使用5个进程,随机访问 ...

  5. python自动重试第三方包retrying

    最近写了一个爬虫,需要连接国外的一个网站,经常出现掉线的情况,自己写了一个自动重连的代码,但感觉不够简洁... 后来就上万能的github,找到了一个第三包,基本能满足我的要求.这个第三方包就是ret ...

  6. 几个大型网站的Feeds(Timeline)设计简单对比

    https://mp.weixin.qq.com/s?__biz=MjM5NzQ3ODAwMQ==&mid=404465806&idx=1&sn=3a68a786138538f ...

  7. Jmeter(二十七)_Beanshell保存响应内容到本地

    利用Jmeter-BeanShell PostProcessor可以提取响应结果并保存到本地文件,这种操作在jmeter做爬虫时非常有用,可以帮助你迅速的获取想要的内容到本地文件! 1:在本地新建一个 ...

  8. nginx配置 location及rewrite规则详解

    1. location正则写法 语法规则: location [=|~|~*|^~] /uri/ { … } =    开头表示精确匹配 ^~  开头表示uri以某个常规字符串开头,理解为匹配 url ...

  9. 微软黑科技强力注入,.NET C#全面支持人工智能

    微软黑科技强力注入,.NET C#全面支持人工智能,AI编程领域开始C#.Py--百花齐放 就像武侠小说中,一个普通人突然得到绝世高手的几十年内力注入,招式还没学,一身内力有点方 Introducin ...

  10. python笔记:#011#循环

    循环 目标 程序的三大流程 while 循环基本使用 break 和 continue while 循环嵌套 01. 程序的三大流程 在程序开发中,一共有三种流程方式: 顺序 -- 从上向下,顺序执行 ...