Merge pull request #132 from nephanim/perf/frontpercent
Improve frontpercent performance
This commit is contained in:
commit
49adcd680a
@ -337,6 +337,48 @@ namespace PluralKit {
|
|||||||
return await conn.QueryAsync<PKSwitch>("select * from switches where system = @System order by timestamp desc limit @Count", new {System = system.Id, Count = count});
|
return await conn.QueryAsync<PKSwitch>("select * from switches where system = @System order by timestamp desc limit @Count", new {System = system.Id, Count = count});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct SwitchMembersListEntry
|
||||||
|
{
|
||||||
|
public int Member;
|
||||||
|
public Instant Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SwitchMembersListEntry>> GetSwitchMembersList(PKSystem system, Instant start, Instant end)
|
||||||
|
{
|
||||||
|
// Wrap multiple commands in a single transaction for performance
|
||||||
|
using (var conn = await _conn.Obtain())
|
||||||
|
using (var tx = conn.BeginTransaction())
|
||||||
|
{
|
||||||
|
// Find the time of the last switch outside the range as it overlaps the range
|
||||||
|
// If no prior switch exists, the lower bound of the range remains the start time
|
||||||
|
var lastSwitch = await conn.QuerySingleOrDefaultAsync<Instant>(
|
||||||
|
@"SELECT COALESCE(MAX(timestamp), @Start)
|
||||||
|
FROM switches
|
||||||
|
WHERE switches.system = @System
|
||||||
|
AND switches.timestamp < @Start",
|
||||||
|
new { System = system.Id, Start = start });
|
||||||
|
|
||||||
|
// Then collect the time and members of all switches that overlap the range
|
||||||
|
var switchMembersEntries = await conn.QueryAsync<SwitchMembersListEntry>(
|
||||||
|
@"SELECT switch_members.member, switches.timestamp
|
||||||
|
FROM switches
|
||||||
|
JOIN switch_members
|
||||||
|
ON switches.id = switch_members.switch
|
||||||
|
WHERE switches.system = @System
|
||||||
|
AND (
|
||||||
|
switches.timestamp >= @Start
|
||||||
|
OR switches.timestamp = @LastSwitch
|
||||||
|
)
|
||||||
|
AND switches.timestamp < @End
|
||||||
|
ORDER BY switches.timestamp DESC",
|
||||||
|
new { System = system.Id, Start = start, End = end, LastSwitch = lastSwitch });
|
||||||
|
|
||||||
|
// Commit and return the list
|
||||||
|
tx.Commit();
|
||||||
|
return switchMembersEntries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<int>> GetSwitchMemberIds(PKSwitch sw)
|
public async Task<IEnumerable<int>> GetSwitchMemberIds(PKSwitch sw)
|
||||||
{
|
{
|
||||||
using (var conn = await _conn.Obtain())
|
using (var conn = await _conn.Obtain())
|
||||||
@ -386,14 +428,8 @@ namespace PluralKit {
|
|||||||
|
|
||||||
public async Task<IEnumerable<SwitchListEntry>> GetTruncatedSwitchList(PKSystem system, Instant periodStart, Instant periodEnd)
|
public async Task<IEnumerable<SwitchListEntry>> GetTruncatedSwitchList(PKSystem system, Instant periodStart, Instant periodEnd)
|
||||||
{
|
{
|
||||||
// TODO: only fetch the necessary switches here
|
// Returns the timestamps and member IDs of switches overlapping the range, in chronological (newest first) order
|
||||||
// todo: this is in general not very efficient LOL
|
var switchMembers = await GetSwitchMembersList(system, periodStart, periodEnd);
|
||||||
// returns switches in chronological (newest first) order
|
|
||||||
var switches = await GetSwitches(system);
|
|
||||||
|
|
||||||
// we skip all switches that happened later than the range end, and taking all the ones that happened after the range start
|
|
||||||
// *BUT ALSO INCLUDING* the last switch *before* the range (that partially overlaps the range period)
|
|
||||||
var switchesInRange = switches.SkipWhile(sw => sw.Timestamp >= periodEnd).TakeWhileIncluding(sw => sw.Timestamp > periodStart).ToList();
|
|
||||||
|
|
||||||
// query DB for all members involved in any of the switches above and collect into a dictionary for future use
|
// query DB for all members involved in any of the switches above and collect into a dictionary for future use
|
||||||
// this makes sure the return list has the same instances of PKMember throughout, which is important for the dictionary
|
// this makes sure the return list has the same instances of PKMember throughout, which is important for the dictionary
|
||||||
@ -401,34 +437,43 @@ namespace PluralKit {
|
|||||||
Dictionary<int, PKMember> memberObjects;
|
Dictionary<int, PKMember> memberObjects;
|
||||||
using (var conn = await _conn.Obtain())
|
using (var conn = await _conn.Obtain())
|
||||||
{
|
{
|
||||||
memberObjects = (await conn.QueryAsync<PKMember>(
|
memberObjects = (
|
||||||
"select distinct members.* from members, switch_members where switch_members.switch = any(@Switches) and switch_members.member = members.id", // lol postgres specific `= any()` syntax
|
await conn.QueryAsync<PKMember>(
|
||||||
new {Switches = switchesInRange.Select(sw => sw.Id).ToList()}))
|
"select * from members where id = any(@Switches)", // lol postgres specific `= any()` syntax
|
||||||
.ToDictionary(m => m.Id);
|
new { Switches = switchMembers.Select(m => m.Member).Distinct().ToList() })
|
||||||
|
).ToDictionary(m => m.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize entries - still need to loop to determine the TimespanEnd below
|
||||||
// we create the entry objects
|
var entries =
|
||||||
var outList = new List<SwitchListEntry>();
|
from item in switchMembers
|
||||||
|
group item by item.Timestamp into g
|
||||||
// loop through every switch that *occurred* in-range and add it to the list
|
select new SwitchListEntry
|
||||||
// end time is the switch *after*'s timestamp - we cheat and start it out at the range end so the first switch in-range "ends" there instead of the one after's start point
|
|
||||||
var endTime = periodEnd;
|
|
||||||
foreach (var switchInRange in switchesInRange)
|
|
||||||
{
|
{
|
||||||
// find the start time of the switch, but clamp it to the range (only applicable to the Last Switch Before Range we include in the TakeWhileIncluding call above)
|
TimespanStart = g.Key,
|
||||||
var switchStartClamped = switchInRange.Timestamp;
|
Members = g.Select(x => memberObjects[x.Member]).ToList()
|
||||||
if (switchStartClamped < periodStart) switchStartClamped = periodStart;
|
};
|
||||||
|
|
||||||
|
// Loop through every switch that overlaps the range and add it to the output list
|
||||||
|
// end time is the *FOLLOWING* switch's timestamp - we cheat by working backwards from the range end, so no dates need to be compared
|
||||||
|
var endTime = periodEnd;
|
||||||
|
var outList = new List<SwitchListEntry>();
|
||||||
|
foreach (var e in entries)
|
||||||
|
{
|
||||||
|
// Override the start time of the switch if it's outside the range (only true for the "out of range" switch we included above)
|
||||||
|
var switchStartClamped = e.TimespanStart < periodStart
|
||||||
|
? periodStart
|
||||||
|
: e.TimespanStart;
|
||||||
|
|
||||||
outList.Add(new SwitchListEntry
|
outList.Add(new SwitchListEntry
|
||||||
{
|
{
|
||||||
Members = (await GetSwitchMemberIds(switchInRange)).Select(id => memberObjects[id]).ToList(),
|
Members = e.Members,
|
||||||
TimespanStart = switchStartClamped,
|
TimespanStart = switchStartClamped,
|
||||||
TimespanEnd = endTime
|
TimespanEnd = endTime
|
||||||
});
|
});
|
||||||
|
|
||||||
// next switch's end is this switch's start
|
// next switch's end is this switch's start (we're working backward in time)
|
||||||
endTime = switchInRange.Timestamp;
|
endTime = e.TimespanStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
return outList;
|
return outList;
|
||||||
|
@ -49,6 +49,10 @@ create table if not exists switches
|
|||||||
system serial not null references systems (id) on delete cascade,
|
system serial not null references systems (id) on delete cascade,
|
||||||
timestamp timestamp not null default (current_timestamp at time zone 'utc')
|
timestamp timestamp not null default (current_timestamp at time zone 'utc')
|
||||||
);
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_switches_system
|
||||||
|
ON switches USING btree (
|
||||||
|
system ASC NULLS LAST
|
||||||
|
) INCLUDE ("timestamp");
|
||||||
|
|
||||||
create table if not exists switch_members
|
create table if not exists switch_members
|
||||||
(
|
(
|
||||||
@ -56,6 +60,10 @@ create table if not exists switch_members
|
|||||||
switch serial not null references switches (id) on delete cascade,
|
switch serial not null references switches (id) on delete cascade,
|
||||||
member serial not null references members (id) on delete cascade
|
member serial not null references members (id) on delete cascade
|
||||||
);
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_switch_members_switch
|
||||||
|
ON switch_members USING btree (
|
||||||
|
switch ASC NULLS LAST
|
||||||
|
) INCLUDE (member);
|
||||||
|
|
||||||
create table if not exists webhooks
|
create table if not exists webhooks
|
||||||
(
|
(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user