| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- // Copyright (C) 2019-2022 Nicola Murino
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License as published
- // by the Free Software Foundation, version 3.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- package dataprovider
- import (
- "sync"
- "time"
- "github.com/drakkan/sftpgo/v2/logger"
- )
- var delayedQuotaUpdater quotaUpdater
- func init() {
- delayedQuotaUpdater = newQuotaUpdater()
- }
- type quotaObject struct {
- size int64
- files int
- }
- type transferQuotaObject struct {
- ulSize int64
- dlSize int64
- }
- type quotaUpdater struct {
- paramsMutex sync.RWMutex
- waitTime time.Duration
- sync.RWMutex
- pendingUserQuotaUpdates map[string]quotaObject
- pendingFolderQuotaUpdates map[string]quotaObject
- pendingTransferQuotaUpdates map[string]transferQuotaObject
- }
- func newQuotaUpdater() quotaUpdater {
- return quotaUpdater{
- pendingUserQuotaUpdates: make(map[string]quotaObject),
- pendingFolderQuotaUpdates: make(map[string]quotaObject),
- pendingTransferQuotaUpdates: make(map[string]transferQuotaObject),
- }
- }
- func (q *quotaUpdater) start() {
- q.setWaitTime(config.DelayedQuotaUpdate)
- go q.loop()
- }
- func (q *quotaUpdater) loop() {
- waitTime := q.getWaitTime()
- providerLog(logger.LevelDebug, "delayed quota update loop started, wait time: %v", waitTime)
- for waitTime > 0 {
- // We do this with a time.Sleep instead of a time.Ticker because we don't know
- // how long each quota processing cycle will take, and we want to make
- // sure we wait the configured seconds between each iteration
- time.Sleep(waitTime)
- providerLog(logger.LevelDebug, "delayed quota update check start")
- q.storeUsersQuota()
- q.storeFoldersQuota()
- q.storeUsersTransferQuota()
- providerLog(logger.LevelDebug, "delayed quota update check end")
- waitTime = q.getWaitTime()
- }
- providerLog(logger.LevelDebug, "delayed quota update loop ended, wait time: %v", waitTime)
- }
- func (q *quotaUpdater) setWaitTime(secs int) {
- q.paramsMutex.Lock()
- defer q.paramsMutex.Unlock()
- q.waitTime = time.Duration(secs) * time.Second
- }
- func (q *quotaUpdater) getWaitTime() time.Duration {
- q.paramsMutex.RLock()
- defer q.paramsMutex.RUnlock()
- return q.waitTime
- }
- func (q *quotaUpdater) resetUserQuota(username string) {
- q.Lock()
- defer q.Unlock()
- delete(q.pendingUserQuotaUpdates, username)
- }
- func (q *quotaUpdater) updateUserQuota(username string, files int, size int64) {
- q.Lock()
- defer q.Unlock()
- obj := q.pendingUserQuotaUpdates[username]
- obj.size += size
- obj.files += files
- if obj.files == 0 && obj.size == 0 {
- delete(q.pendingUserQuotaUpdates, username)
- return
- }
- q.pendingUserQuotaUpdates[username] = obj
- }
- func (q *quotaUpdater) getUserPendingQuota(username string) (int, int64) {
- q.RLock()
- defer q.RUnlock()
- obj := q.pendingUserQuotaUpdates[username]
- return obj.files, obj.size
- }
- func (q *quotaUpdater) resetFolderQuota(name string) {
- q.Lock()
- defer q.Unlock()
- delete(q.pendingFolderQuotaUpdates, name)
- }
- func (q *quotaUpdater) updateFolderQuota(name string, files int, size int64) {
- q.Lock()
- defer q.Unlock()
- obj := q.pendingFolderQuotaUpdates[name]
- obj.size += size
- obj.files += files
- if obj.files == 0 && obj.size == 0 {
- delete(q.pendingFolderQuotaUpdates, name)
- return
- }
- q.pendingFolderQuotaUpdates[name] = obj
- }
- func (q *quotaUpdater) getFolderPendingQuota(name string) (int, int64) {
- q.RLock()
- defer q.RUnlock()
- obj := q.pendingFolderQuotaUpdates[name]
- return obj.files, obj.size
- }
- func (q *quotaUpdater) resetUserTransferQuota(username string) {
- q.Lock()
- defer q.Unlock()
- delete(q.pendingTransferQuotaUpdates, username)
- }
- func (q *quotaUpdater) updateUserTransferQuota(username string, ulSize, dlSize int64) {
- q.Lock()
- defer q.Unlock()
- obj := q.pendingTransferQuotaUpdates[username]
- obj.ulSize += ulSize
- obj.dlSize += dlSize
- if obj.ulSize == 0 && obj.dlSize == 0 {
- delete(q.pendingTransferQuotaUpdates, username)
- return
- }
- q.pendingTransferQuotaUpdates[username] = obj
- }
- func (q *quotaUpdater) getUserPendingTransferQuota(username string) (int64, int64) {
- q.RLock()
- defer q.RUnlock()
- obj := q.pendingTransferQuotaUpdates[username]
- return obj.ulSize, obj.dlSize
- }
- func (q *quotaUpdater) getUsernames() []string {
- q.RLock()
- defer q.RUnlock()
- result := make([]string, 0, len(q.pendingUserQuotaUpdates))
- for username := range q.pendingUserQuotaUpdates {
- result = append(result, username)
- }
- return result
- }
- func (q *quotaUpdater) getFoldernames() []string {
- q.RLock()
- defer q.RUnlock()
- result := make([]string, 0, len(q.pendingFolderQuotaUpdates))
- for name := range q.pendingFolderQuotaUpdates {
- result = append(result, name)
- }
- return result
- }
- func (q *quotaUpdater) getTransferQuotaUsernames() []string {
- q.RLock()
- defer q.RUnlock()
- result := make([]string, 0, len(q.pendingTransferQuotaUpdates))
- for username := range q.pendingTransferQuotaUpdates {
- result = append(result, username)
- }
- return result
- }
- func (q *quotaUpdater) storeUsersQuota() {
- for _, username := range q.getUsernames() {
- files, size := q.getUserPendingQuota(username)
- if size != 0 || files != 0 {
- err := provider.updateQuota(username, files, size, false)
- if err != nil {
- providerLog(logger.LevelWarn, "unable to update quota delayed for user %#v: %v", username, err)
- continue
- }
- q.updateUserQuota(username, -files, -size)
- }
- }
- }
- func (q *quotaUpdater) storeFoldersQuota() {
- for _, name := range q.getFoldernames() {
- files, size := q.getFolderPendingQuota(name)
- if size != 0 || files != 0 {
- err := provider.updateFolderQuota(name, files, size, false)
- if err != nil {
- providerLog(logger.LevelWarn, "unable to update quota delayed for folder %#v: %v", name, err)
- continue
- }
- q.updateFolderQuota(name, -files, -size)
- }
- }
- }
- func (q *quotaUpdater) storeUsersTransferQuota() {
- for _, username := range q.getTransferQuotaUsernames() {
- ulSize, dlSize := q.getUserPendingTransferQuota(username)
- if ulSize != 0 || dlSize != 0 {
- err := provider.updateTransferQuota(username, ulSize, dlSize, false)
- if err != nil {
- providerLog(logger.LevelWarn, "unable to update transfer quota delayed for user %#v: %v", username, err)
- continue
- }
- q.updateUserTransferQuota(username, -ulSize, -dlSize)
- }
- }
- }
|