<commit_before>package cachedblobstore

import (
	"fmt"
	"io"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/nyaxt/otaru/blobstore"
	fl "github.com/nyaxt/otaru/flags"
	"github.com/nyaxt/otaru/logger"
	"github.com/nyaxt/otaru/util"
	"github.com/nyaxt/otaru/util/cancellable"
)

type CacheEntryState int

const (
	CacheEntryUninitialized CacheEntryState = iota
	CacheEntryInvalidating
	CacheEntryErrored
	CacheEntryErroredClosed
	CacheEntryClean
	CacheEntryWriteInProgress
	CacheEntryWritebackInProgress
	CacheEntryStaleWritebackInProgress
	CacheEntryDirty
	CacheEntryDirtyClosing
	CacheEntryClosing
	CacheEntryClosed
)

func (s CacheEntryState) ShouldBeListed() bool {
	return s == CacheEntryInvalidating ||
		s == CacheEntryClean ||
		s == CacheEntryWriteInProgress ||
		s == CacheEntryWritebackInProgress ||
		s == CacheEntryStaleWritebackInProgress ||
		s == CacheEntryDirty
}

func (s CacheEntryState) NeedsSync() bool {
	return s == CacheEntryDirty || s == CacheEntryWriteInProgress
}

func (s CacheEntryState) String() string {
	switch s {
	case CacheEntryUninitialized:
		return "Uninitialized"
	case CacheEntryInvalidating:
		return "Invalidating"
	case CacheEntryErrored:
		return "Errored"
	case CacheEntryErroredClosed:
		return "ErroredClosed"
	case CacheEntryClean:
		return "Clean"
	case CacheEntryWriteInProgress:
		return "WriteInProgress"
	case CacheEntryWritebackInProgress:
		return "WritebackInProgress"
	case CacheEntryStaleWritebackInProgress:
		return "StaleWritebackInProgress"
	case CacheEntryDirty:
		return "Dirty"
	case CacheEntryDirtyClosing:
		return "DirtyClosing"
	case CacheEntryClosing:
		return "Closing"
	case CacheEntryClosed:
		return "Closed"
	default:
		return "<unknown>"
	}
}

type CachedBlobEntry struct {
	mu               sync.Mutex
	progressCond     *sync.Cond
	cancelInvalidate func()

	cbs      *CachedBlobStore
	blobpath string
	cachebh  blobstore.BlobHandle

	state CacheEntryState

	bloblen  int64
	validlen int64

	lastUsed  time.Time
	lastWrite time.Time
	lastSync  time.Time
	syncCount int

	handles map[*CachedBlobHandle]struct{}
}

func NewCachedBlobEntry(blobpath string) *CachedBlobEntry {
	be := &CachedBlobEntry{
		state:    CacheEntryUninitialized,
		blobpath: blobpath,
		bloblen:  -1,
	}
	be.progressCond = sync.NewCond(&be.mu)
	return be
}

func (be *CachedBlobEntry) updateState(newState CacheEntryState) {
	logger.Debugf(mylog, "Cache state \"%s\": %v -> %v", be.blobpath, be.state, newState)
	switch be.state {
	case CacheEntryUninitialized:
		switch newState {
		case CacheEntryInvalidating, CacheEntryClean, CacheEntryErrored:
			break
		default:
			goto Unexpected
		}
	case CacheEntryInvalidating:
		switch newState {
		case CacheEntryClean, CacheEntryErrored:
			break
		default:
			goto Unexpected
		}
	case CacheEntryErrored:
		switch newState {
		case CacheEntryErroredClosed:
			break
		default:
			goto Unexpected
		}
	case CacheEntryErroredClosed:
		goto Unexpected
	case CacheEntryClean:
		switch newState {
		case CacheEntryWriteInProgress, CacheEntryClosing:
			break
		default:
			goto Unexpected
		}
	case CacheEntryWriteInProgress:
		switch newState {
		case CacheEntryDirty:
			break
		default:
			goto Unexpected
		}
	case CacheEntryWritebackInProgress:
		switch newState {
		case CacheEntryClean, CacheEntryStaleWritebackInProgress, CacheEntryErrored:
			break
		default:
			goto Unexpected
		}
	case CacheEntryStaleWritebackInProgress:
		switch newState {
		case CacheEntryDirty:
			break
		default:
			goto Unexpected
		}
	case CacheEntryDirty:<commit_msg>Allow StaleWbIP -> StaleWbIP state transfer. Attempt to fix #63<commit_after>package cachedblobstore

import (
	"fmt"
	"io"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/nyaxt/otaru/blobstore"
	fl "github.com/nyaxt/otaru/flags"
	"github.com/nyaxt/otaru/logger"
	"github.com/nyaxt/otaru/util"
	"github.com/nyaxt/otaru/util/cancellable"
)

type CacheEntryState int

const (
	CacheEntryUninitialized CacheEntryState = iota
	CacheEntryInvalidating
	CacheEntryErrored
	CacheEntryErroredClosed
	CacheEntryClean
	CacheEntryWriteInProgress
	CacheEntryWritebackInProgress
	CacheEntryStaleWritebackInProgress
	CacheEntryDirty
	CacheEntryDirtyClosing
	CacheEntryClosing
	CacheEntryClosed
)

func (s CacheEntryState) ShouldBeListed() bool {
	return s == CacheEntryInvalidating ||
		s == CacheEntryClean ||
		s == CacheEntryWriteInProgress ||
		s == CacheEntryWritebackInProgress ||
		s == CacheEntryStaleWritebackInProgress ||
		s == CacheEntryDirty
}

func (s CacheEntryState) NeedsSync() bool {
	return s == CacheEntryDirty || s == CacheEntryWriteInProgress
}

func (s CacheEntryState) String() string {
	switch s {
	case CacheEntryUninitialized:
		return "Uninitialized"
	case CacheEntryInvalidating:
		return "Invalidating"
	case CacheEntryErrored:
		return "Errored"
	case CacheEntryErroredClosed:
		return "ErroredClosed"
	case CacheEntryClean:
		return "Clean"
	case CacheEntryWriteInProgress:
		return "WriteInProgress"
	case CacheEntryWritebackInProgress:
		return "WritebackInProgress"
	case CacheEntryStaleWritebackInProgress:
		return "StaleWritebackInProgress"
	case CacheEntryDirty:
		return "Dirty"
	case CacheEntryDirtyClosing:
		return "DirtyClosing"
	case CacheEntryClosing:
		return "Closing"
	case CacheEntryClosed:
		return "Closed"
	default:
		return "<unknown>"
	}
}

type CachedBlobEntry struct {
	mu               sync.Mutex
	progressCond     *sync.Cond
	cancelInvalidate func()

	cbs      *CachedBlobStore
	blobpath string
	cachebh  blobstore.BlobHandle

	state CacheEntryState

	bloblen  int64
	validlen int64

	lastUsed  time.Time
	lastWrite time.Time
	lastSync  time.Time
	syncCount int

	handles map[*CachedBlobHandle]struct{}
}

func NewCachedBlobEntry(blobpath string) *CachedBlobEntry {
	be := &CachedBlobEntry{
		state:    CacheEntryUninitialized,
		blobpath: blobpath,
		bloblen:  -1,
	}
	be.progressCond = sync.NewCond(&be.mu)
	return be
}

func (be *CachedBlobEntry) updateState(newState CacheEntryState) {
	logger.Debugf(mylog, "Cache state \"%s\": %v -> %v", be.blobpath, be.state, newState)
	switch be.state {
	case CacheEntryUninitialized:
		switch newState {
		case CacheEntryInvalidating, CacheEntryClean, CacheEntryErrored:
			break
		default:
			goto Unexpected
		}
	case CacheEntryInvalidating:
		switch newState {
		case CacheEntryClean, CacheEntryErrored:
			break
		default:
			goto Unexpected
		}
	case CacheEntryErrored:
		switch newState {
		case CacheEntryErroredClosed:
			break
		default:
			goto Unexpected
		}
	case CacheEntryErroredClosed:
		goto Unexpected
	case CacheEntryClean:
		switch newState {
		case CacheEntryWriteInProgress, CacheEntryClosing:
			break
		default:
			goto Unexpected
		}
	case CacheEntryWriteInProgress:
		switch newState {
		case CacheEntryDirty:
			break
		default:
			goto Unexpected
		}
	case CacheEntryWritebackInProgress:
		switch newState {
		case CacheEntryClean, CacheEntryStaleWritebackInProgress, CacheEntryErrored:
			break
		default:
			goto Unexpected
		}
	case CacheEntryStaleWritebackInProgress:
		switch newState {
		case CacheEntryDirty, CacheEntryStaleWritebackInProgress:
			break
		default:
			goto Unexpected
		}
	case CacheEntryDirty:
{'fraction_non_alphanumeric': 0.07154765212559044, 'fraction_numerical': 0.0016671297582661851, 'mean_word_length': 4.819725141471301, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6335302', 'n_tokens_mistral': 2571, 'n_tokens_neox': 2352, 'n_words': 534}