github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfsblock/quota.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfsblock
     6  
     7  import (
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/kbfs/kbfscodec"
    11  	"github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  // UsageType indicates the type of usage that quota manager is keeping stats of
    15  type UsageType int
    16  
    17  const (
    18  	// UsageWrite indicates a data block is written (written blocks include archived blocks)
    19  	UsageWrite UsageType = iota
    20  	// UsageArchive indicates an existing (data) block is archived
    21  	UsageArchive
    22  	// UsageRead indicates a block is read
    23  	UsageRead
    24  	// UsageMDWrite indicates a MD block is written
    25  	UsageMDWrite
    26  	// UsageGitWrite indicates a git block is written
    27  	UsageGitWrite
    28  	// UsageGitArchive indicates an existing git block is archived
    29  	UsageGitArchive
    30  	// NumUsage indicates the number of usage types
    31  	NumUsage
    32  )
    33  
    34  // UsageStat tracks the amount of bytes/blocks used, broken down by usage types
    35  type UsageStat struct {
    36  	Bytes  map[UsageType]int64
    37  	Blocks map[UsageType]int64
    38  	// Mtime is in unix nanoseconds
    39  	Mtime int64
    40  }
    41  
    42  // NewUsageStat creates a new UsageStat
    43  func NewUsageStat() *UsageStat {
    44  	return &UsageStat{
    45  		Bytes:  make(map[UsageType]int64),
    46  		Blocks: make(map[UsageType]int64),
    47  	}
    48  }
    49  
    50  // UsageStatFromProtocol converts the RPC format into the local format.
    51  func UsageStatFromProtocol(stat keybase1.UsageStat) *UsageStat {
    52  	u := &UsageStat{
    53  		Bytes: map[UsageType]int64{
    54  			UsageWrite:      stat.Bytes.Write,
    55  			UsageArchive:    stat.Bytes.Archive,
    56  			UsageRead:       stat.Bytes.Read,
    57  			UsageMDWrite:    stat.Bytes.MdWrite,
    58  			UsageGitWrite:   stat.Bytes.GitWrite,
    59  			UsageGitArchive: stat.Bytes.GitArchive,
    60  		},
    61  		Blocks: map[UsageType]int64{
    62  			UsageWrite:      stat.Blocks.Write,
    63  			UsageArchive:    stat.Blocks.Archive,
    64  			UsageRead:       stat.Blocks.Read,
    65  			UsageMDWrite:    stat.Blocks.MdWrite,
    66  			UsageGitWrite:   stat.Blocks.GitWrite,
    67  			UsageGitArchive: stat.Blocks.GitArchive,
    68  		},
    69  		Mtime: keybase1.FromTime(stat.Mtime).UnixNano(),
    70  	}
    71  
    72  	return u
    73  }
    74  
    75  // NonZero checks whether UsageStat has accumulated any usage info
    76  func (u *UsageStat) NonZero() bool {
    77  	for i := UsageType(0); i < NumUsage; i++ {
    78  		if u.Bytes[i] != 0 {
    79  			return true
    80  		}
    81  	}
    82  	return false
    83  }
    84  
    85  // ToProtocol converts this stat to the RPC protocol format.
    86  func (u *UsageStat) ToProtocol() (res keybase1.UsageStat) {
    87  	res.Mtime = keybase1.ToTime(time.Unix(0, u.Mtime))
    88  
    89  	res.Bytes.Write = u.Bytes[UsageWrite]
    90  	res.Bytes.Archive = u.Bytes[UsageArchive]
    91  	res.Bytes.Read = u.Bytes[UsageRead]
    92  	res.Bytes.MdWrite = u.Bytes[UsageMDWrite]
    93  	res.Bytes.GitWrite = u.Bytes[UsageGitWrite]
    94  	res.Bytes.GitArchive = u.Bytes[UsageGitArchive]
    95  
    96  	res.Blocks.Write = u.Blocks[UsageWrite]
    97  	res.Blocks.Archive = u.Blocks[UsageArchive]
    98  	res.Blocks.Read = u.Blocks[UsageRead]
    99  	res.Blocks.MdWrite = u.Blocks[UsageMDWrite]
   100  	res.Blocks.GitWrite = u.Blocks[UsageGitWrite]
   101  	res.Blocks.GitArchive = u.Blocks[UsageGitArchive]
   102  
   103  	return res
   104  }
   105  
   106  // AccumOne records the usage of one block, whose size is denoted by change
   107  // A positive change means the block is newly added, negative means the block
   108  // is deleted. If archive is true, it means the block is archived.
   109  func (u *UsageStat) AccumOne(change int, usage UsageType) {
   110  	if change == 0 {
   111  		return
   112  	}
   113  	if usage == UsageMDWrite || usage >= NumUsage {
   114  		return
   115  	}
   116  	u.Bytes[usage] += int64(change)
   117  	if change > 0 {
   118  		u.Blocks[usage]++
   119  	} else {
   120  		u.Blocks[usage]--
   121  	}
   122  }
   123  
   124  // Accum combines changes to the existing QuotaInfo object using accumulation function accumF.
   125  func (u *UsageStat) Accum(another *UsageStat, accumF func(int64, int64) int64) {
   126  	if another == nil {
   127  		return
   128  	}
   129  	for k, v := range another.Bytes {
   130  		u.Bytes[k] = accumF(u.Bytes[k], v)
   131  	}
   132  	for k, v := range another.Blocks {
   133  		u.Blocks[k] = accumF(u.Blocks[k], v)
   134  	}
   135  }
   136  
   137  // QuotaInfo contains a user's quota usage information
   138  type QuotaInfo struct {
   139  	Folders  map[string]*UsageStat
   140  	Total    *UsageStat
   141  	Limit    int64
   142  	GitLimit int64
   143  }
   144  
   145  // NewQuotaInfo returns a newly constructed QuotaInfo.
   146  func NewQuotaInfo() *QuotaInfo {
   147  	return &QuotaInfo{
   148  		Folders: make(map[string]*UsageStat),
   149  		Total:   NewUsageStat(),
   150  	}
   151  }
   152  
   153  // QuotaInfoFromProtocol converts the RPC format into the local format.
   154  func QuotaInfoFromProtocol(info keybase1.BlockQuotaInfo) *QuotaInfo {
   155  	u := &QuotaInfo{
   156  		Folders:  make(map[string]*UsageStat, len(info.Folders)),
   157  		Total:    UsageStatFromProtocol(info.Total),
   158  		Limit:    info.Limit,
   159  		GitLimit: info.GitLimit,
   160  	}
   161  	for _, stat := range info.Folders {
   162  		u.Folders[stat.FolderID] = UsageStatFromProtocol(stat.Stats)
   163  	}
   164  	return u
   165  }
   166  
   167  // AccumOne combines one quota charge to the existing QuotaInfo
   168  func (u *QuotaInfo) AccumOne(change int, folder string, usage UsageType) {
   169  	if _, ok := u.Folders[folder]; !ok {
   170  		u.Folders[folder] = NewUsageStat()
   171  	}
   172  	u.Folders[folder].AccumOne(change, usage)
   173  	u.Total.AccumOne(change, usage)
   174  }
   175  
   176  // Accum combines changes to the existing QuotaInfo object using accumulation function accumF.
   177  func (u *QuotaInfo) Accum(another *QuotaInfo, accumF func(int64, int64) int64) {
   178  	if another == nil {
   179  		return
   180  	}
   181  	if u.Total == nil {
   182  		u.Total = NewUsageStat()
   183  	}
   184  	u.Total.Accum(another.Total, accumF)
   185  	for f, change := range another.Folders {
   186  		if _, ok := u.Folders[f]; !ok {
   187  			u.Folders[f] = NewUsageStat()
   188  		}
   189  		u.Folders[f].Accum(change, accumF)
   190  	}
   191  }
   192  
   193  // ToBytes marshals this QuotaInfo
   194  func (u *QuotaInfo) ToBytes(codec kbfscodec.Codec) ([]byte, error) {
   195  	return codec.Encode(u)
   196  }
   197  
   198  // ToProtocol converts this info to the RPC protocol format.
   199  func (u *QuotaInfo) ToProtocol() (res keybase1.BlockQuotaInfo) {
   200  	res.Limit = u.Limit
   201  	res.GitLimit = u.GitLimit
   202  	res.Total = u.Total.ToProtocol()
   203  
   204  	res.Folders = make([]keybase1.FolderUsageStat, 0, len(u.Folders))
   205  	for f, us := range u.Folders {
   206  		res.Folders = append(res.Folders, keybase1.FolderUsageStat{
   207  			FolderID: f,
   208  			Stats:    us.ToProtocol(),
   209  		})
   210  	}
   211  
   212  	return res
   213  }
   214  
   215  // QuotaInfoDecode decodes b into a QuotaInfo
   216  func QuotaInfoDecode(b []byte, codec kbfscodec.Codec) (
   217  	*QuotaInfo, error) {
   218  	var info QuotaInfo
   219  	err := codec.Decode(b, &info)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	return &info, nil
   225  }