package volume

import (
    "github.com/syndtr/goleveldb/leveldb"
    "sync"
    "encoding/binary"
    "github.com/syndtr/goleveldb/leveldb/util"
    "errors"
    "path/filepath"
    "strconv"
    "fmt"
)

const (
    ReversedsizeOffsetPrefix = '\x11' //key= "\x01"+Reversesize(8 byte)+offset(8 byte) value=[]
    OffsetSizePrefix = '\x22' //key= "\x02"+offset(8 byte)+size(8 byte) value=[]
)

/*
status主要存储空闲块的offset size
 */
type Status struct {
    path       string
    db         *leveldb.DB
    spaceMutex sync.Mutex
}
//创建空闲文件  元数据
func NewStatus(dir string, vid uint64) (status *Status, err error) {
    path := filepath.Join(dir, strconv.FormatUint(vid, 10) + ".status")
    status = new(Status)
    status.path = path
    status.db, err = leveldb.OpenFile(path, nil)
    return status, err
}
//分配空间
func (s *Status)newSpace(size uint64) (offset uint64, err error) {
    s.spaceMutex.Lock()
    defer s.spaceMutex.Unlock()

    //这里根据size倒序存储,使最大的空间最先被获取
    iter := s.db.NewIterator(util.BytesPrefix([]byte{ReversedsizeOffsetPrefix}), nil)
    defer iter.Release()
    iter.Next()
    key := iter.Key()
    if len(key) == 0 {
        return 0, errors.New("can't get free space")
    }

    freeSize := binary.BigEndian.Uint64(key[1:9]) ^ (^uint64(0))
    if freeSize < size {
        return 0, errors.New("can't get free space")
    }
    offset = binary.BigEndian.Uint64(key[9:])

    transaction, err := s.db.OpenTransaction()
    if err != nil {
        return 0, err
    }

    transaction.Delete(key, nil)

    key = getOffsetSizeKey(offset, freeSize)
    transaction.Delete(key, nil)

    //如果空闲块大小大于所请求大小,则将剩下空闲块,重新记录
    if freeSize > size {
        key = getReversedsizeOffset(offset + size, freeSize - size)
        transaction.Put(key, nil, nil)

        key = getOffsetSizeKey(offset + size, freeSize - size)
        transaction.Put(key, nil, nil)
    }

    err = transaction.Commit()
    return offset, err
}
//  释放文件空间
func (s *Status)freeSpace(offset uint64, size uint64) error {
    s.spaceMutex.Lock()
    defer s.spaceMutex.Unlock()
       //这里根据size倒序存储,使最大的空间最先被获取
    iter := s.db.NewIterator(util.BytesPrefix([]byte{OffsetSizePrefix}), nil)
    defer iter.Release()
    key := getOffsetSizeKey(offset, 0)
    iter.Seek(key)
    transaction, err := s.db.OpenTransaction()
    if err != nil {
        return err
    }
    //如果与下一块空闲块相邻,则合成一块空闲块
    key = iter.Key()
    if len(key) != 0 {
        nOffset := binary.BigEndian.Uint64(key[1:9])
        nSize := binary.BigEndian.Uint64(key[9:])
        if nOffset < offset + size {
            panic(fmt.Errorf("nOffset: %d < offset: %d + size: %d", nOffset, offset, size))
        } else if nOffset == offset + size {
            transaction.Delete(key, nil)
            size += nSize
            key = getReversedsizeOffset(nOffset, nSize)
            transaction.Delete(key, nil)
        }
    }
    //如果与上一块空闲块相邻,则合成一块空闲块
    iter.Prev()
    key = iter.Key()
    if len(key) != 0 {
        pOffset := binary.BigEndian.Uint64(key[1:9])
        pSize := binary.BigEndian.Uint64(key[9:])
        if pOffset + pSize > offset {
            panic(fmt.Errorf("pOffset: %d + pSize: %d > offset: %d", pOffset, pSize, offset))
        } else if pOffset + pSize == offset {
            transaction.Delete(key, nil)
            offset = pOffset
            size += pSize
            key = getReversedsizeOffset(pOffset, pSize)
            transaction.Delete(key, nil)
        }
    }
    key = getOffsetSizeKey(offset, size)
    transaction.Put(key, nil, nil)
    key = getReversedsizeOffset(offset, size)
    transaction.Put(key, nil, nil)
    return transaction.Commit()
}
//
func (s *Status)getMaxFreeSpace() uint64 {
    iter := s.db.NewIterator(util.BytesPrefix([]byte{ReversedsizeOffsetPrefix}), nil)
    defer iter.Release()
    iter.Next()
    key := iter.Key()
    if len(key) == 0 {
        return 0
    }
    freeSize := binary.BigEndian.Uint64(key[1:9]) ^ (^uint64(0))
    return freeSize
}
//获取偏移量 文件key
func getOffsetSizeKey(offset, size uint64) []byte {
    key := make([]byte, 1 + 16)
    key[0] = OffsetSizePrefix
    binary.BigEndian.PutUint64(key[1:9], offset)
    binary.BigEndian.PutUint64(key[9:], size)
    return key
}
//
func getReversedsizeOffset(offset, size uint64) []byte {
    key := make([]byte, 1 + 16)
    key[0] = ReversedsizeOffsetPrefix
    binary.BigEndian.PutUint64(key[9:], offset)
    binary.BigEndian.PutUint64(key[1:9], size ^ (^uint64(0)))
    return key
}

status.go的更多相关文章

  1. http status code

    属于转载 http status code:200:成功,服务器已成功处理了请求,通常这表示服务器提供了请求的网页 404:未找到,服务器未找到 201-206都表示服务器成功处理了请求的状态代码,说 ...

  2. ORACLE中STATUS为INACTIVE但是SERVER为SHARED状态的会话浅析

    我们知道当ORACLE数据库启用共享服务器模式时,通过共享服务器模式连接到数据库的会话是有一些特征的.在v$session里面,其SERVER的状态一般为SHARED和NONE, 为SHARED时,表 ...

  3. 重定向Http status code 303 和 302

    http 302 http 303 Http 302 302是一个普通的重定向代码.直观的看来是,请求者(浏览器或者模拟http请求)发起一个请求,然后服务端重定向到另一个地址.而事实上,服务端仅仅是 ...

  4. C#、JAVA操作Hadoop(HDFS、Map/Reduce)真实过程概述。组件、源码下载。无法解决:Response status code does not indicate success: 500。

    一.Hadoop环境配置概述 三台虚拟机,操作系统为:Ubuntu 16.04. Hadoop版本:2.7.2 NameNode:192.168.72.132 DataNode:192.168.72. ...

  5. 关于XHR对象中status范围的记录

    if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ // 成功执行区域 // 2XX表示有效响应 ...

  6. Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xe" for details

    thinkphp 在Apache上配置启用伪静态,重启Apache1 restart 竟然失败了,报错 Job for httpd.service failed because the control ...

  7. ajax response status list [转载]

    比较理想的解释方法应该以"状态:任务(目标)+过程+表现(或特征)"的表达模式来对这几个状态进行定义  [全文]  在<Pragmatic Ajax A Web 2.0 Pr ...

  8. Zabbix监控redis status

    概述 zabbix采用Trapper方式监控redis status 原理 redis-cli info命令得到redis服务器的统计信息,脚本对信息分两部分处理: (1)# Keyspace部分为Z ...

  9. Zabbix监控nginx-rtmp status(json版)

    与前面的文章 zabbix监控nginx-rtmp status(html版)区别只在于取值的页面不一样 http://127.0.0.1:81/control/get/all_streams sta ...

  10. 查看Mysql版本号 (最简单的是status )

    一.使用命令行模式进入mysql会看到最开始的提示符;二.命令行中使用status可以看到;三.使用系统函数等等,   查看版本信息 #1使用命令行模式进入mysql会看到最开始的提示符 Your M ...

随机推荐

  1. for循环嵌套讲解:

    1.for循环嵌套讲解: class ForForDemo {     public static void main(String[] args)     {         //大圈套小圈思想: ...

  2. Day15 Javascipt内容补充

    JavaScript函数: 函数: function 函数名(a,b,c){ 执行代码 } 1,如何去找到标签 Dom直接选择器: 1,找到标签 #获取单个元素 document.getElement ...

  3. add-apt-repository出Exception问题

    参考:http://blog.sina.com.cn/s/blog_5388923c0100nu8h.html 症状: xxxx@xxxxx:~$ sudo add-apt-repository pp ...

  4. ScalaPB(2): 在scala中用gRPC实现微服务

    gRPC是google开源提供的一个RPC软件框架,它的特点是极大简化了传统RPC的开发流程和代码量,使用户可以免除许多陷阱并聚焦于实际应用逻辑中.作为一种google的最新RPC解决方案,gRPC具 ...

  5. JSON数据Key排序

    /// <summary> /// JSON格式化重新排序 /// </summary> /// <param name="jobj">原始JS ...

  6. myeclipse 的Customize Perspective 没有反应

    MyEclipse 2014 工具栏里的Quick Access老是跳上跳下的,弄得我很烦,所以就想自定义一下工具栏,结果 window--> customize perspective 没用 ...

  7. MySQL的变量分类总结

    在MySQL中,my.cnf是参数文件(Option Files),类似于ORACLE数据库中的spfile.pfile参数文件,照理说,参数文件my.cnf中的都是系统参数(这种称呼比较符合思维习惯 ...

  8. mapreduce shuffle 和sort 详解

        MapReduce 框架的核心步骤主要分两部分:Map 和Reduce.当你向MapReduce 框架提交一个计算作业时,它会首先把计算作业拆分成若干个Map 任务,然后分配到不同的节点上去执 ...

  9. 40多行python代码开发一个区块链。

    40多行python代码开发一个区块链?可信吗?我们将通过Python 2动手开发实现一个迷你区块链来帮你真正理解区块链技术的核心原理.python开发区块链的源代码保存在Github. 尽管有人认为 ...

  10. jsp页面日期格式不正确

    第一种: 如果是从数据库获取的时间(数据库中日期格式是乱的)可以在数据库取数据时   进行格式化   例如  ;TO_CHAR(SYSDATE,'YYYY-MM-DD') 第二种: 在数据库取出数据后 ...