Improve wakuMessage query performance (#13)

This commit is contained in:
Szymon Szlachtowicz 2021-08-16 15:39:06 +02:00 committed by GitHub
parent 8cca55ccd5
commit 54e1ca8ca2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 39 deletions

View File

@ -9,12 +9,39 @@ import { TimedPollVoteMsg } from './models/TimedPollVoteMsg'
import TimedPollVote from './utils/proto/TimedPollVote'
import { DetailedTimedPoll } from './models/DetailedTimedPoll'
function decodeWakuMessages<T>(
messages: WakuMessage[] | null | undefined,
decoder: { decode: (payload: Uint8Array | undefined, timestamp: Date | undefined) => T | undefined }
) {
return (
messages
?.map((msg) => decoder.decode(msg.payload, msg.timestamp))
.filter((poll: T | undefined): poll is T => !!poll) ?? []
)
}
async function receiveNewWakuMessages(lastTimestamp: number, topic: string, waku: Waku | undefined) {
const messages = await waku?.store.queryHistory({ contentTopics: [topic] })
if (messages) {
messages.sort((a, b) => (a.timestamp && b.timestamp && a.timestamp?.getTime() < b.timestamp?.getTime() ? 1 : -1))
const lastMessageIndex = messages.findIndex((message) => message.timestamp?.getTime() === lastTimestamp)
const newMessages = lastMessageIndex === -1 ? messages : messages.slice(0, lastMessageIndex)
return newMessages
}
return []
}
class WakuVoting {
private appName: string
private waku: Waku | undefined
public tokenAddress: string
private pollInitTopic: string
private timedPollVoteTopic: string
private timedPollInitMessages: PollInitMsg[] = []
private timedPollVotesMessages: TimedPollVoteMsg[] = []
private static async createWaku() {
const waku = await Waku.create()
const nodes = await getStatusFleetNodes()
@ -64,13 +91,14 @@ class WakuVoting {
}
private async getTimedPolls() {
const messages = await this.waku?.store.queryHistory({ contentTopics: [this.pollInitTopic] })
return (
messages
?.filter((e): e is WakuMessage & { payload: Uint8Array } => !!e?.payload)
.map((msg) => PollInit.decode(msg.payload, msg.timestamp))
.filter((poll): poll is PollInitMsg => !!poll) ?? []
)
const lastTimestamp = this.timedPollInitMessages?.[0]?.timestamp ?? 0
const newMessages = await receiveNewWakuMessages(lastTimestamp, this.pollInitTopic, this.waku)
const newPollInitMessages = decodeWakuMessages(newMessages, PollInit)
if (newPollInitMessages.length > 0) {
this.timedPollInitMessages = [...newPollInitMessages, ...this.timedPollInitMessages]
}
return this.timedPollInitMessages
}
public async sendTimedPollVote(
@ -92,13 +120,14 @@ class WakuVoting {
}
private async getTimedPollsVotes() {
const messages = await this.waku?.store.queryHistory({ contentTopics: [this.timedPollVoteTopic] })
return (
messages
?.filter((e): e is WakuMessage & { payload: Uint8Array } => !!e?.payload)
.map((msg) => TimedPollVote.decode(msg.payload, msg.timestamp))
.filter((poll): poll is TimedPollVoteMsg => !!poll) ?? []
)
const lastTimestamp = this.timedPollVotesMessages?.[0]?.timestamp ?? 0
const newMessages = await receiveNewWakuMessages(lastTimestamp, this.timedPollVoteTopic, this.waku)
const newVoteMessages = decodeWakuMessages(newMessages, TimedPollVote)
if (newVoteMessages.length > 0) {
this.timedPollVotesMessages = [...newVoteMessages, ...this.timedPollVotesMessages]
}
return this.timedPollVotesMessages
}
public async getDetailedTimedPolls() {

View File

@ -39,7 +39,7 @@ export function createSignMsgParams(message: Message) {
if (message.tokenAmount) {
msgParams.message = { ...msgParams.message, tokenAmount: message.tokenAmount.toString() }
msgParams.types.Mail.push({ name: 'minToken', type: 'uint256' })
msgParams.types.Mail.push({ name: 'tokenAmount', type: 'uint256' })
}
return msgParams
}

View File

@ -48,10 +48,13 @@ export function encode(pollInit: PollInitMsg) {
}
export function decode(
payload: Uint8Array,
payload: Uint8Array | undefined,
timestamp: Date | undefined,
recoverFunction?: ({ data, sig }: { data: any; sig: string }) => string
) {
if (!payload) {
return undefined
}
try {
const msg = proto.PollInit.decode(payload)
if (!timestamp || timestamp.getTime() != msg.timestamp) {

View File

@ -33,10 +33,13 @@ export function encode(timedPollVote: TimedPollVoteMsg) {
}
export function decode(
payload: Uint8Array,
payload: Uint8Array | undefined,
timestamp: Date | undefined,
recoverFunction?: ({ data, sig }: { data: any; sig: string }) => string
) {
if (!payload) {
return undefined
}
try {
const msg = proto.TimedPollVote.decode(payload)
if (!timestamp || timestamp.getTime() != msg.timestamp) {

View File

@ -16,11 +16,15 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
const [selectedAnswer, setSelectedAnswer] = useState(0)
const [tokenAmount, setTokenAmount] = useState(0)
const [address, setAddress] = useState('')
const [userInVoters, setUserInVoters] = useState(false)
useEffect(() => {
signer.getAddress().then((e) => setAddress(e))
}, [signer])
useEffect(() => {
setUserInVoters(!!poll.votesMessages.find((vote) => vote.voter === address))
}, [poll])
return (
<PollWrapper>
<PollTitle>
@ -28,7 +32,7 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
<PollTypeWrapper>{poll.poll.pollType === PollType.WEIGHTED ? 'WEIGHTED' : 'NON WEIGHTED'}</PollTypeWrapper>
</PollTitle>
<PollAnswersWrapper>
{!poll.votesMessages.find((vote) => vote.voter === address) && (
{!userInVoters && (
<div>
<div onChange={(e) => setSelectedAnswer(Number.parseInt((e.target as any).value ?? 0))}>
{poll.poll.answers.map((answer, idx) => {
@ -51,7 +55,7 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
)}
</div>
)}
{poll.votesMessages.find((vote) => vote.voter === address) && (
{userInVoters && (
<div>
Results
{poll.answers.map((answer, idx) => {
@ -65,21 +69,23 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
</div>
)}
</PollAnswersWrapper>
<button
onClick={() => {
if (wakuVoting) {
wakuVoting.sendTimedPollVote(
signer,
poll.poll.id,
selectedAnswer,
poll.poll.pollType === PollType.WEIGHTED ? BigNumber.from(tokenAmount) : undefined
)
}
}}
>
{' '}
Vote
</button>
{!userInVoters && (
<button
onClick={() => {
if (wakuVoting) {
wakuVoting.sendTimedPollVote(
signer,
poll.poll.id,
selectedAnswer,
poll.poll.pollType === PollType.WEIGHTED ? BigNumber.from(tokenAmount) : undefined
)
}
}}
>
{' '}
Vote
</button>
)}
</PollWrapper>
)
}

View File

@ -19,7 +19,7 @@ export function PollList({ wakuVoting, signer }: PollListProps) {
if (wakuVoting) {
setPolls(await wakuVoting.getDetailedTimedPolls())
}
}, 1000)
}, 5000)
return () => clearInterval(interval)
}, [wakuVoting])

View File

@ -30,9 +30,9 @@ function Example({ appName }: ExampleProps) {
provider.send('eth_requestAccounts', [])
}, [])
return (
<Wrapper>
<Wrapper onClick={() => showNewPollBox && setShowNewPollBox(false)}>
{showNewPollBox && (
<NewPollBoxWrapper>
<NewPollBoxWrapper onClick={(e) => e.stopPropagation()}>
<NewPollBox>
<NewPollBoxTitle>
Question
@ -83,7 +83,7 @@ function Example({ appName }: ExampleProps) {
</NewPollBox>
</NewPollBoxWrapper>
)}
<button onClick={() => setShowNewPollBox(true)}>New Poll </button>
<button onClick={() => setShowNewPollBox(true)}>New Poll</button>
<PollList wakuVoting={wakuVoting} signer={signer} />
</Wrapper>
)