mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-22 11:46:40 +00:00
[bugfix] fix inconsistent calculated cache sizes (#2115)
* use calculated exampleTime instead of `time.Now()` to ensure no locale data, retweak cache ratios * update envparsing test * update default cache memory to 100MiB * fix envparsing with latest cache target default --------- Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
912a104aed
commit
815b5291e0
|
@ -236,8 +236,8 @@ cache:
|
||||||
# within. This is based on estimated sizes of
|
# within. This is based on estimated sizes of
|
||||||
# in-memory objects, and so NOT AT ALL EXACT.
|
# in-memory objects, and so NOT AT ALL EXACT.
|
||||||
# Examples: ["100MiB", "200MiB", "500MiB", "1GiB"]
|
# Examples: ["100MiB", "200MiB", "500MiB", "1GiB"]
|
||||||
# Default: "200MiB"
|
# Default: "100MiB"
|
||||||
memory-target: "200MiB"
|
memory-target: "100MiB"
|
||||||
|
|
||||||
######################
|
######################
|
||||||
##### WEB CONFIG #####
|
##### WEB CONFIG #####
|
||||||
|
|
58
internal/cache/gts.go
vendored
58
internal/cache/gts.go
vendored
|
@ -269,7 +269,7 @@ func (c *GTSCaches) initAccount() {
|
||||||
config.GetCacheAccountMemRatio(),
|
config.GetCacheAccountMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Account cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.account = result.New([]result.Lookup{
|
c.account = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -296,7 +296,8 @@ func (c *GTSCaches) initAccountNote() {
|
||||||
sizeofAccountNote(), // model in-mem size.
|
sizeofAccountNote(), // model in-mem size.
|
||||||
config.GetCacheAccountNoteMemRatio(),
|
config.GetCacheAccountNoteMemRatio(),
|
||||||
)
|
)
|
||||||
log.Infof(nil, "AccountNote cache size = %d", cap)
|
|
||||||
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.accountNote = result.New([]result.Lookup{
|
c.accountNote = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -316,7 +317,8 @@ func (c *GTSCaches) initApplication() {
|
||||||
sizeofApplication(), // model in-mem size.
|
sizeofApplication(), // model in-mem size.
|
||||||
config.GetCacheApplicationMemRatio(),
|
config.GetCacheApplicationMemRatio(),
|
||||||
)
|
)
|
||||||
log.Infof(nil, "Application cache size = %d", cap)
|
|
||||||
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.application = result.New([]result.Lookup{
|
c.application = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -337,7 +339,7 @@ func (c *GTSCaches) initBlock() {
|
||||||
config.GetCacheBlockMemRatio(),
|
config.GetCacheBlockMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Block cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.block = result.New([]result.Lookup{
|
c.block = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -360,7 +362,7 @@ func (c *GTSCaches) initBlockIDs() {
|
||||||
config.GetCacheBlockIDsMemRatio(),
|
config.GetCacheBlockIDsMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Block IDs cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.blockIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
c.blockIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
||||||
0,
|
0,
|
||||||
|
@ -374,7 +376,7 @@ func (c *GTSCaches) initBoostOfIDs() {
|
||||||
config.GetCacheBoostOfIDsMemRatio(),
|
config.GetCacheBoostOfIDsMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "BoostofIDs cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.boostOfIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
c.boostOfIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
||||||
0,
|
0,
|
||||||
|
@ -393,7 +395,7 @@ func (c *GTSCaches) initEmoji() {
|
||||||
config.GetCacheEmojiMemRatio(),
|
config.GetCacheEmojiMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Emoji cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.emoji = result.New([]result.Lookup{
|
c.emoji = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -417,7 +419,7 @@ func (c *GTSCaches) initEmojiCategory() {
|
||||||
config.GetCacheEmojiCategoryMemRatio(),
|
config.GetCacheEmojiCategoryMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "EmojiCategory cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.emojiCategory = result.New([]result.Lookup{
|
c.emojiCategory = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -438,7 +440,7 @@ func (c *GTSCaches) initFollow() {
|
||||||
config.GetCacheFollowMemRatio(),
|
config.GetCacheFollowMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Follow cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.follow = result.New([]result.Lookup{
|
c.follow = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -461,7 +463,7 @@ func (c *GTSCaches) initFollowIDs() {
|
||||||
config.GetCacheFollowIDsMemRatio(),
|
config.GetCacheFollowIDsMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Follow IDs cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.followIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
c.followIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
||||||
0,
|
0,
|
||||||
|
@ -476,7 +478,7 @@ func (c *GTSCaches) initFollowRequest() {
|
||||||
config.GetCacheFollowRequestMemRatio(),
|
config.GetCacheFollowRequestMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "FollowRequest cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.followRequest = result.New([]result.Lookup{
|
c.followRequest = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -499,7 +501,7 @@ func (c *GTSCaches) initFollowRequestIDs() {
|
||||||
config.GetCacheFollowRequestIDsMemRatio(),
|
config.GetCacheFollowRequestIDsMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Follow Request IDs cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.followRequestIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
c.followRequestIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
||||||
0,
|
0,
|
||||||
|
@ -513,7 +515,7 @@ func (c *GTSCaches) initInReplyToIDs() {
|
||||||
config.GetCacheInReplyToIDsMemRatio(),
|
config.GetCacheInReplyToIDsMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "InReplyTo IDs cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.inReplyToIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
c.inReplyToIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
||||||
0,
|
0,
|
||||||
|
@ -528,7 +530,7 @@ func (c *GTSCaches) initInstance() {
|
||||||
config.GetCacheInstanceMemRatio(),
|
config.GetCacheInstanceMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Instance cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.instance = result.New([]result.Lookup{
|
c.instance = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -549,7 +551,7 @@ func (c *GTSCaches) initList() {
|
||||||
config.GetCacheListMemRatio(),
|
config.GetCacheListMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "List cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.list = result.New([]result.Lookup{
|
c.list = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -569,7 +571,7 @@ func (c *GTSCaches) initListEntry() {
|
||||||
config.GetCacheListEntryMemRatio(),
|
config.GetCacheListEntryMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "ListEntry cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.listEntry = result.New([]result.Lookup{
|
c.listEntry = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -591,7 +593,7 @@ func (c *GTSCaches) initMarker() {
|
||||||
config.GetCacheMarkerMemRatio(),
|
config.GetCacheMarkerMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Marker cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.marker = result.New([]result.Lookup{
|
c.marker = result.New([]result.Lookup{
|
||||||
{Name: "AccountID.Name"},
|
{Name: "AccountID.Name"},
|
||||||
|
@ -611,7 +613,7 @@ func (c *GTSCaches) initMedia() {
|
||||||
config.GetCacheMediaMemRatio(),
|
config.GetCacheMediaMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Media cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.media = result.New([]result.Lookup{
|
c.media = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -631,7 +633,7 @@ func (c *GTSCaches) initMention() {
|
||||||
config.GetCacheMentionMemRatio(),
|
config.GetCacheMentionMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Mention cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.mention = result.New([]result.Lookup{
|
c.mention = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -651,7 +653,7 @@ func (c *GTSCaches) initNotification() {
|
||||||
config.GetCacheNotificationMemRatio(),
|
config.GetCacheNotificationMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Notification cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.notification = result.New([]result.Lookup{
|
c.notification = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -672,7 +674,7 @@ func (c *GTSCaches) initReport() {
|
||||||
config.GetCacheReportMemRatio(),
|
config.GetCacheReportMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Report cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.report = result.New([]result.Lookup{
|
c.report = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -692,7 +694,7 @@ func (c *GTSCaches) initStatus() {
|
||||||
config.GetCacheStatusMemRatio(),
|
config.GetCacheStatusMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Status cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.status = result.New([]result.Lookup{
|
c.status = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -715,7 +717,7 @@ func (c *GTSCaches) initStatusFave() {
|
||||||
config.GetCacheStatusFaveMemRatio(),
|
config.GetCacheStatusFaveMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "StatusFave cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.statusFave = result.New([]result.Lookup{
|
c.statusFave = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -736,7 +738,7 @@ func (c *GTSCaches) initStatusFaveIDs() {
|
||||||
config.GetCacheStatusFaveIDsMemRatio(),
|
config.GetCacheStatusFaveIDsMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "StatusFave IDs cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.statusFaveIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
c.statusFaveIDs = &SliceCache[string]{Cache: simple.New[string, []string](
|
||||||
0,
|
0,
|
||||||
|
@ -751,7 +753,7 @@ func (c *GTSCaches) initTag() {
|
||||||
config.GetCacheTagMemRatio(),
|
config.GetCacheTagMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Tag cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.tag = result.New([]result.Lookup{
|
c.tag = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -772,7 +774,7 @@ func (c *GTSCaches) initTombstone() {
|
||||||
config.GetCacheTombstoneMemRatio(),
|
config.GetCacheTombstoneMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Tombstone cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.tombstone = result.New([]result.Lookup{
|
c.tombstone = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -793,7 +795,7 @@ func (c *GTSCaches) initUser() {
|
||||||
config.GetCacheUserMemRatio(),
|
config.GetCacheUserMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "User cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.user = result.New([]result.Lookup{
|
c.user = result.New([]result.Lookup{
|
||||||
{Name: "ID"},
|
{Name: "ID"},
|
||||||
|
@ -817,7 +819,7 @@ func (c *GTSCaches) initWebfinger() {
|
||||||
config.GetCacheWebfingerMemRatio(),
|
config.GetCacheWebfingerMemRatio(),
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Infof(nil, "Webfinger cache size = %d", cap)
|
log.Infof(nil, "cache size = %d", cap)
|
||||||
|
|
||||||
c.webfinger = ttl.New[string, string](
|
c.webfinger = ttl.New[string, string](
|
||||||
0,
|
0,
|
||||||
|
|
124
internal/cache/size.go
vendored
124
internal/cache/size.go
vendored
|
@ -66,6 +66,22 @@
|
||||||
sizeofResultKey = 2 * sizeofIDStr
|
sizeofResultKey = 2 * sizeofIDStr
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Example time calculated at ~ 14th August, 2023. Because if
|
||||||
|
// we use `time.Now()` in our structs below, it populates
|
||||||
|
// them with locale data which throws-off size calculations.
|
||||||
|
//
|
||||||
|
// This is because the locale data is (relatively) very large
|
||||||
|
// in-memory, but it's global "singletons" ptr'd to by the time
|
||||||
|
// structs, so inconsequential to our calculated cache size.
|
||||||
|
// Unfortunately the size.Of() function is not aware of this!
|
||||||
|
exampleTime = time.Time{}.Add(1692010328 * time.Second)
|
||||||
|
|
||||||
|
// stop trying to collapse this var
|
||||||
|
// block, gofmt, you motherfucker.
|
||||||
|
_ = interface{}(nil)
|
||||||
|
)
|
||||||
|
|
||||||
// calculateSliceCacheMax calculates the maximum capacity for a slice cache with given individual ratio.
|
// calculateSliceCacheMax calculates the maximum capacity for a slice cache with given individual ratio.
|
||||||
func calculateSliceCacheMax(ratio float64) int {
|
func calculateSliceCacheMax(ratio float64) int {
|
||||||
return calculateCacheMax(sizeofIDStr, sizeofIDSlice, ratio)
|
return calculateCacheMax(sizeofIDStr, sizeofIDSlice, ratio)
|
||||||
|
@ -89,12 +105,12 @@ func calculateResultCacheMax(structSz uintptr, ratio float64) int {
|
||||||
// The result cache wraps each struct result in a wrapping
|
// The result cache wraps each struct result in a wrapping
|
||||||
// struct with further information, and possible error. This
|
// struct with further information, and possible error. This
|
||||||
// also needs to be taken into account when calculating value.
|
// also needs to be taken into account when calculating value.
|
||||||
const resultValueOverhead = unsafe.Sizeof(&struct {
|
resultValueOverhead := uintptr(size.Of(&struct {
|
||||||
_ int64
|
_ int64
|
||||||
_ []any
|
_ []any
|
||||||
_ any
|
_ any
|
||||||
_ error
|
_ error
|
||||||
}{})
|
}{}))
|
||||||
|
|
||||||
return calculateCacheMax(
|
return calculateCacheMax(
|
||||||
pkeySz+totalLookupKeySz,
|
pkeySz+totalLookupKeySz,
|
||||||
|
@ -194,9 +210,9 @@ func sizeofAccount() uintptr {
|
||||||
Note: exampleText,
|
Note: exampleText,
|
||||||
NoteRaw: exampleText,
|
NoteRaw: exampleText,
|
||||||
Memorial: func() *bool { ok := false; return &ok }(),
|
Memorial: func() *bool { ok := false; return &ok }(),
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
FetchedAt: time.Now(),
|
FetchedAt: exampleTime,
|
||||||
Bot: func() *bool { ok := true; return &ok }(),
|
Bot: func() *bool { ok := true; return &ok }(),
|
||||||
Locked: func() *bool { ok := true; return &ok }(),
|
Locked: func() *bool { ok := true; return &ok }(),
|
||||||
Discoverable: func() *bool { ok := false; return &ok }(),
|
Discoverable: func() *bool { ok := false; return &ok }(),
|
||||||
|
@ -214,9 +230,9 @@ func sizeofAccount() uintptr {
|
||||||
PrivateKey: &rsa.PrivateKey{},
|
PrivateKey: &rsa.PrivateKey{},
|
||||||
PublicKey: &rsa.PublicKey{},
|
PublicKey: &rsa.PublicKey{},
|
||||||
PublicKeyURI: exampleURI,
|
PublicKeyURI: exampleURI,
|
||||||
SensitizedAt: time.Time{},
|
SensitizedAt: exampleTime,
|
||||||
SilencedAt: time.Now(),
|
SilencedAt: exampleTime,
|
||||||
SuspendedAt: time.Now(),
|
SuspendedAt: exampleTime,
|
||||||
HideCollections: func() *bool { ok := true; return &ok }(),
|
HideCollections: func() *bool { ok := true; return &ok }(),
|
||||||
SuspensionOrigin: exampleID,
|
SuspensionOrigin: exampleID,
|
||||||
EnableRSS: func() *bool { ok := true; return &ok }(),
|
EnableRSS: func() *bool { ok := true; return &ok }(),
|
||||||
|
@ -235,8 +251,8 @@ func sizeofAccountNote() uintptr {
|
||||||
func sizeofApplication() uintptr {
|
func sizeofApplication() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Application{
|
return uintptr(size.Of(>smodel.Application{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Name: exampleUsername,
|
Name: exampleUsername,
|
||||||
Website: exampleURI,
|
Website: exampleURI,
|
||||||
RedirectURI: exampleURI,
|
RedirectURI: exampleURI,
|
||||||
|
@ -249,8 +265,8 @@ func sizeofApplication() uintptr {
|
||||||
func sizeofBlock() uintptr {
|
func sizeofBlock() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Block{
|
return uintptr(size.Of(>smodel.Block{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
URI: exampleURI,
|
URI: exampleURI,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
|
@ -262,8 +278,8 @@ func sizeofEmoji() uintptr {
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
Shortcode: exampleTextSmall,
|
Shortcode: exampleTextSmall,
|
||||||
Domain: exampleURI,
|
Domain: exampleURI,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
ImageRemoteURL: exampleURI,
|
ImageRemoteURL: exampleURI,
|
||||||
ImageStaticRemoteURL: exampleURI,
|
ImageStaticRemoteURL: exampleURI,
|
||||||
ImageURL: exampleURI,
|
ImageURL: exampleURI,
|
||||||
|
@ -272,7 +288,7 @@ func sizeofEmoji() uintptr {
|
||||||
ImageStaticPath: exampleURI,
|
ImageStaticPath: exampleURI,
|
||||||
ImageContentType: "image/png",
|
ImageContentType: "image/png",
|
||||||
ImageStaticContentType: "image/png",
|
ImageStaticContentType: "image/png",
|
||||||
ImageUpdatedAt: time.Now(),
|
ImageUpdatedAt: exampleTime,
|
||||||
Disabled: func() *bool { ok := false; return &ok }(),
|
Disabled: func() *bool { ok := false; return &ok }(),
|
||||||
URI: "http://localhost:8080/emoji/01F8MH9H8E4VG3KDYJR9EGPXCQ",
|
URI: "http://localhost:8080/emoji/01F8MH9H8E4VG3KDYJR9EGPXCQ",
|
||||||
VisibleInPicker: func() *bool { ok := true; return &ok }(),
|
VisibleInPicker: func() *bool { ok := true; return &ok }(),
|
||||||
|
@ -285,16 +301,16 @@ func sizeofEmojiCategory() uintptr {
|
||||||
return uintptr(size.Of(>smodel.EmojiCategory{
|
return uintptr(size.Of(>smodel.EmojiCategory{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
Name: exampleUsername,
|
Name: exampleUsername,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sizeofFollow() uintptr {
|
func sizeofFollow() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Follow{
|
return uintptr(size.Of(>smodel.Follow{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
ShowReblogs: func() *bool { ok := true; return &ok }(),
|
ShowReblogs: func() *bool { ok := true; return &ok }(),
|
||||||
|
@ -306,8 +322,8 @@ func sizeofFollow() uintptr {
|
||||||
func sizeofFollowRequest() uintptr {
|
func sizeofFollowRequest() uintptr {
|
||||||
return uintptr(size.Of(>smodel.FollowRequest{
|
return uintptr(size.Of(>smodel.FollowRequest{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
ShowReblogs: func() *bool { ok := true; return &ok }(),
|
ShowReblogs: func() *bool { ok := true; return &ok }(),
|
||||||
|
@ -319,8 +335,8 @@ func sizeofFollowRequest() uintptr {
|
||||||
func sizeofInstance() uintptr {
|
func sizeofInstance() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Instance{
|
return uintptr(size.Of(>smodel.Instance{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Domain: exampleURI,
|
Domain: exampleURI,
|
||||||
URI: exampleURI,
|
URI: exampleURI,
|
||||||
Title: exampleTextSmall,
|
Title: exampleTextSmall,
|
||||||
|
@ -335,8 +351,8 @@ func sizeofInstance() uintptr {
|
||||||
func sizeofList() uintptr {
|
func sizeofList() uintptr {
|
||||||
return uintptr(size.Of(>smodel.List{
|
return uintptr(size.Of(>smodel.List{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Title: exampleTextSmall,
|
Title: exampleTextSmall,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
RepliesPolicy: gtsmodel.RepliesPolicyFollowed,
|
RepliesPolicy: gtsmodel.RepliesPolicyFollowed,
|
||||||
|
@ -346,8 +362,8 @@ func sizeofList() uintptr {
|
||||||
func sizeofListEntry() uintptr {
|
func sizeofListEntry() uintptr {
|
||||||
return uintptr(size.Of(>smodel.ListEntry{
|
return uintptr(size.Of(>smodel.ListEntry{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
ListID: exampleID,
|
ListID: exampleID,
|
||||||
FollowID: exampleID,
|
FollowID: exampleID,
|
||||||
}))
|
}))
|
||||||
|
@ -357,7 +373,7 @@ func sizeofMarker() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Marker{
|
return uintptr(size.Of(>smodel.Marker{
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
Name: gtsmodel.MarkerNameHome,
|
Name: gtsmodel.MarkerNameHome,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Version: 0,
|
Version: 0,
|
||||||
LastReadID: exampleID,
|
LastReadID: exampleID,
|
||||||
}))
|
}))
|
||||||
|
@ -369,8 +385,8 @@ func sizeofMedia() uintptr {
|
||||||
StatusID: exampleID,
|
StatusID: exampleID,
|
||||||
URL: exampleURI,
|
URL: exampleURI,
|
||||||
RemoteURL: exampleURI,
|
RemoteURL: exampleURI,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Type: gtsmodel.FileTypeImage,
|
Type: gtsmodel.FileTypeImage,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
Description: exampleText,
|
Description: exampleText,
|
||||||
|
@ -379,12 +395,12 @@ func sizeofMedia() uintptr {
|
||||||
File: gtsmodel.File{
|
File: gtsmodel.File{
|
||||||
Path: exampleURI,
|
Path: exampleURI,
|
||||||
ContentType: "image/jpeg",
|
ContentType: "image/jpeg",
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
},
|
},
|
||||||
Thumbnail: gtsmodel.Thumbnail{
|
Thumbnail: gtsmodel.Thumbnail{
|
||||||
Path: exampleURI,
|
Path: exampleURI,
|
||||||
ContentType: "image/jpeg",
|
ContentType: "image/jpeg",
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
URL: exampleURI,
|
URL: exampleURI,
|
||||||
RemoteURL: exampleURI,
|
RemoteURL: exampleURI,
|
||||||
},
|
},
|
||||||
|
@ -398,8 +414,8 @@ func sizeofMention() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Mention{
|
return uintptr(size.Of(>smodel.Mention{
|
||||||
ID: exampleURI,
|
ID: exampleURI,
|
||||||
StatusID: exampleURI,
|
StatusID: exampleURI,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
OriginAccountID: exampleURI,
|
OriginAccountID: exampleURI,
|
||||||
OriginAccountURI: exampleURI,
|
OriginAccountURI: exampleURI,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
|
@ -413,7 +429,7 @@ func sizeofNotification() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Notification{
|
return uintptr(size.Of(>smodel.Notification{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
NotificationType: gtsmodel.NotificationFave,
|
NotificationType: gtsmodel.NotificationFave,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
OriginAccountID: exampleID,
|
OriginAccountID: exampleID,
|
||||||
StatusID: exampleID,
|
StatusID: exampleID,
|
||||||
|
@ -424,8 +440,8 @@ func sizeofNotification() uintptr {
|
||||||
func sizeofReport() uintptr {
|
func sizeofReport() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Report{
|
return uintptr(size.Of(>smodel.Report{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
URI: exampleURI,
|
URI: exampleURI,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
|
@ -433,7 +449,7 @@ func sizeofReport() uintptr {
|
||||||
StatusIDs: []string{exampleID, exampleID, exampleID},
|
StatusIDs: []string{exampleID, exampleID, exampleID},
|
||||||
Forwarded: func() *bool { ok := true; return &ok }(),
|
Forwarded: func() *bool { ok := true; return &ok }(),
|
||||||
ActionTaken: exampleText,
|
ActionTaken: exampleText,
|
||||||
ActionTakenAt: time.Now(),
|
ActionTakenAt: exampleTime,
|
||||||
ActionTakenByAccountID: exampleID,
|
ActionTakenByAccountID: exampleID,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -449,9 +465,9 @@ func sizeofStatus() uintptr {
|
||||||
TagIDs: []string{exampleID, exampleID, exampleID},
|
TagIDs: []string{exampleID, exampleID, exampleID},
|
||||||
MentionIDs: []string{},
|
MentionIDs: []string{},
|
||||||
EmojiIDs: []string{exampleID, exampleID, exampleID},
|
EmojiIDs: []string{exampleID, exampleID, exampleID},
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
FetchedAt: time.Now(),
|
FetchedAt: exampleTime,
|
||||||
Local: func() *bool { ok := false; return &ok }(),
|
Local: func() *bool { ok := false; return &ok }(),
|
||||||
AccountURI: exampleURI,
|
AccountURI: exampleURI,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
|
@ -476,7 +492,7 @@ func sizeofStatus() uintptr {
|
||||||
func sizeofStatusFave() uintptr {
|
func sizeofStatusFave() uintptr {
|
||||||
return uintptr(size.Of(>smodel.StatusFave{
|
return uintptr(size.Of(>smodel.StatusFave{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
TargetAccountID: exampleID,
|
TargetAccountID: exampleID,
|
||||||
StatusID: exampleID,
|
StatusID: exampleID,
|
||||||
|
@ -488,8 +504,8 @@ func sizeofTag() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Tag{
|
return uintptr(size.Of(>smodel.Tag{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
Name: exampleUsername,
|
Name: exampleUsername,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Useable: func() *bool { ok := true; return &ok }(),
|
Useable: func() *bool { ok := true; return &ok }(),
|
||||||
Listable: func() *bool { ok := true; return &ok }(),
|
Listable: func() *bool { ok := true; return &ok }(),
|
||||||
}))
|
}))
|
||||||
|
@ -498,8 +514,8 @@ func sizeofTag() uintptr {
|
||||||
func sizeofTombstone() uintptr {
|
func sizeofTombstone() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Tombstone{
|
return uintptr(size.Of(>smodel.Tombstone{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Domain: exampleUsername,
|
Domain: exampleUsername,
|
||||||
URI: exampleURI,
|
URI: exampleURI,
|
||||||
}))
|
}))
|
||||||
|
@ -517,29 +533,29 @@ func sizeofVisibility() uintptr {
|
||||||
func sizeofUser() uintptr {
|
func sizeofUser() uintptr {
|
||||||
return uintptr(size.Of(>smodel.User{
|
return uintptr(size.Of(>smodel.User{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: exampleTime,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: exampleTime,
|
||||||
Email: exampleURI,
|
Email: exampleURI,
|
||||||
AccountID: exampleID,
|
AccountID: exampleID,
|
||||||
EncryptedPassword: exampleTextSmall,
|
EncryptedPassword: exampleTextSmall,
|
||||||
CurrentSignInAt: time.Now(),
|
CurrentSignInAt: exampleTime,
|
||||||
LastSignInAt: time.Now(),
|
LastSignInAt: exampleTime,
|
||||||
InviteID: exampleID,
|
InviteID: exampleID,
|
||||||
ChosenLanguages: []string{"en", "fr", "jp"},
|
ChosenLanguages: []string{"en", "fr", "jp"},
|
||||||
FilteredLanguages: []string{"en", "fr", "jp"},
|
FilteredLanguages: []string{"en", "fr", "jp"},
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
CreatedByApplicationID: exampleID,
|
CreatedByApplicationID: exampleID,
|
||||||
LastEmailedAt: time.Now(),
|
LastEmailedAt: exampleTime,
|
||||||
ConfirmationToken: exampleTextSmall,
|
ConfirmationToken: exampleTextSmall,
|
||||||
ConfirmationSentAt: time.Now(),
|
ConfirmationSentAt: exampleTime,
|
||||||
ConfirmedAt: time.Now(),
|
ConfirmedAt: exampleTime,
|
||||||
UnconfirmedEmail: exampleURI,
|
UnconfirmedEmail: exampleURI,
|
||||||
Moderator: func() *bool { ok := true; return &ok }(),
|
Moderator: func() *bool { ok := true; return &ok }(),
|
||||||
Admin: func() *bool { ok := true; return &ok }(),
|
Admin: func() *bool { ok := true; return &ok }(),
|
||||||
Disabled: func() *bool { ok := true; return &ok }(),
|
Disabled: func() *bool { ok := true; return &ok }(),
|
||||||
Approved: func() *bool { ok := true; return &ok }(),
|
Approved: func() *bool { ok := true; return &ok }(),
|
||||||
ResetPasswordToken: exampleTextSmall,
|
ResetPasswordToken: exampleTextSmall,
|
||||||
ResetPasswordSentAt: time.Now(),
|
ResetPasswordSentAt: exampleTime,
|
||||||
ExternalID: exampleID,
|
ExternalID: exampleID,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@
|
||||||
// Rough memory target that the total
|
// Rough memory target that the total
|
||||||
// size of all State.Caches will attempt
|
// size of all State.Caches will attempt
|
||||||
// to remain with. Emphasis on *rough*.
|
// to remain with. Emphasis on *rough*.
|
||||||
MemoryTarget: 200 * bytesize.MiB,
|
MemoryTarget: 100 * bytesize.MiB,
|
||||||
|
|
||||||
// These ratios signal what percentage
|
// These ratios signal what percentage
|
||||||
// of the available cache target memory
|
// of the available cache target memory
|
||||||
|
@ -145,32 +145,32 @@
|
||||||
// when TODO items in the size.go source
|
// when TODO items in the size.go source
|
||||||
// file have been addressed, these should
|
// file have been addressed, these should
|
||||||
// be able to make some more sense :D
|
// be able to make some more sense :D
|
||||||
AccountMemRatio: 18,
|
AccountMemRatio: 5,
|
||||||
AccountNoteMemRatio: 0.1,
|
AccountNoteMemRatio: 1,
|
||||||
ApplicationMemRatio: 0.1,
|
ApplicationMemRatio: 0.1,
|
||||||
BlockMemRatio: 3,
|
BlockMemRatio: 2,
|
||||||
BlockIDsMemRatio: 3,
|
BlockIDsMemRatio: 3,
|
||||||
BoostOfIDsMemRatio: 3,
|
BoostOfIDsMemRatio: 3,
|
||||||
EmojiMemRatio: 3,
|
EmojiMemRatio: 3,
|
||||||
EmojiCategoryMemRatio: 0.1,
|
EmojiCategoryMemRatio: 0.1,
|
||||||
FollowMemRatio: 4,
|
FollowMemRatio: 2,
|
||||||
FollowIDsMemRatio: 4,
|
FollowIDsMemRatio: 4,
|
||||||
FollowRequestMemRatio: 2,
|
FollowRequestMemRatio: 2,
|
||||||
FollowRequestIDsMemRatio: 2,
|
FollowRequestIDsMemRatio: 2,
|
||||||
InReplyToIDsMemRatio: 3,
|
InReplyToIDsMemRatio: 3,
|
||||||
InstanceMemRatio: 1,
|
InstanceMemRatio: 1,
|
||||||
ListMemRatio: 3,
|
ListMemRatio: 1,
|
||||||
ListEntryMemRatio: 3,
|
ListEntryMemRatio: 2,
|
||||||
MarkerMemRatio: 0.5,
|
MarkerMemRatio: 0.5,
|
||||||
MediaMemRatio: 4,
|
MediaMemRatio: 4,
|
||||||
MentionMemRatio: 5,
|
MentionMemRatio: 2,
|
||||||
NotificationMemRatio: 5,
|
NotificationMemRatio: 2,
|
||||||
ReportMemRatio: 1,
|
ReportMemRatio: 1,
|
||||||
StatusMemRatio: 18,
|
StatusMemRatio: 5,
|
||||||
StatusFaveMemRatio: 5,
|
StatusFaveMemRatio: 2,
|
||||||
StatusFaveIDsMemRatio: 3,
|
StatusFaveIDsMemRatio: 3,
|
||||||
TagMemRatio: 3,
|
TagMemRatio: 2,
|
||||||
TombstoneMemRatio: 2,
|
TombstoneMemRatio: 0.5,
|
||||||
UserMemRatio: 0.25,
|
UserMemRatio: 0.25,
|
||||||
WebfingerMemRatio: 0.1,
|
WebfingerMemRatio: 0.1,
|
||||||
VisibilityMemRatio: 2,
|
VisibilityMemRatio: 2,
|
||||||
|
|
|
@ -18,32 +18,32 @@ EXPECT=$(cat << "EOF"
|
||||||
"application-name": "gts",
|
"application-name": "gts",
|
||||||
"bind-address": "127.0.0.1",
|
"bind-address": "127.0.0.1",
|
||||||
"cache": {
|
"cache": {
|
||||||
"account-mem-ratio": 18,
|
"account-mem-ratio": 5,
|
||||||
"account-note-mem-ratio": 0.1,
|
"account-note-mem-ratio": 1,
|
||||||
"application-mem-ratio": 0.1,
|
"application-mem-ratio": 0.1,
|
||||||
"block-mem-ratio": 3,
|
"block-mem-ratio": 3,
|
||||||
"boost-of-ids-mem-ratio": 3,
|
"boost-of-ids-mem-ratio": 3,
|
||||||
"emoji-category-mem-ratio": 0.1,
|
"emoji-category-mem-ratio": 0.1,
|
||||||
"emoji-mem-ratio": 3,
|
"emoji-mem-ratio": 3,
|
||||||
"follow-ids-mem-ratio": 4,
|
"follow-ids-mem-ratio": 4,
|
||||||
"follow-mem-ratio": 4,
|
"follow-mem-ratio": 2,
|
||||||
"follow-request-ids-mem-ratio": 2,
|
"follow-request-ids-mem-ratio": 2,
|
||||||
"follow-request-mem-ratio": 2,
|
"follow-request-mem-ratio": 2,
|
||||||
"in-reply-to-ids-mem-ratio": 3,
|
"in-reply-to-ids-mem-ratio": 3,
|
||||||
"instance-mem-ratio": 1,
|
"instance-mem-ratio": 1,
|
||||||
"list-entry-mem-ratio": 3,
|
"list-entry-mem-ratio": 2,
|
||||||
"list-mem-ratio": 3,
|
"list-mem-ratio": 1,
|
||||||
"marker-mem-ratio": 0.5,
|
"marker-mem-ratio": 0.5,
|
||||||
"media-mem-ratio": 4,
|
"media-mem-ratio": 4,
|
||||||
"memory-target": 209715200,
|
"memory-target": 104857600,
|
||||||
"mention-mem-ratio": 5,
|
"mention-mem-ratio": 2,
|
||||||
"notification-mem-ratio": 5,
|
"notification-mem-ratio": 2,
|
||||||
"report-mem-ratio": 1,
|
"report-mem-ratio": 1,
|
||||||
"status-fave-ids-mem-ratio": 3,
|
"status-fave-ids-mem-ratio": 3,
|
||||||
"status-fave-mem-ratio": 5,
|
"status-fave-mem-ratio": 2,
|
||||||
"status-mem-ratio": 18,
|
"status-mem-ratio": 5,
|
||||||
"tag-mem-ratio": 3,
|
"tag-mem-ratio": 2,
|
||||||
"tombstone-mem-ratio": 2,
|
"tombstone-mem-ratio": 0.5,
|
||||||
"user-mem-ratio": 0.25,
|
"user-mem-ratio": 0.25,
|
||||||
"visibility-mem-ratio": 2,
|
"visibility-mem-ratio": 2,
|
||||||
"webfinger-mem-ratio": 0.1
|
"webfinger-mem-ratio": 0.1
|
||||||
|
|
Loading…
Reference in a new issue