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 TimedPollVote from './utils/proto/TimedPollVote'
import { DetailedTimedPoll } from './models/DetailedTimedPoll' 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 { class WakuVoting {
private appName: string private appName: string
private waku: Waku | undefined private waku: Waku | undefined
public tokenAddress: string public tokenAddress: string
private pollInitTopic: string private pollInitTopic: string
private timedPollVoteTopic: string private timedPollVoteTopic: string
private timedPollInitMessages: PollInitMsg[] = []
private timedPollVotesMessages: TimedPollVoteMsg[] = []
private static async createWaku() { private static async createWaku() {
const waku = await Waku.create() const waku = await Waku.create()
const nodes = await getStatusFleetNodes() const nodes = await getStatusFleetNodes()
@ -64,13 +91,14 @@ class WakuVoting {
} }
private async getTimedPolls() { private async getTimedPolls() {
const messages = await this.waku?.store.queryHistory({ contentTopics: [this.pollInitTopic] }) const lastTimestamp = this.timedPollInitMessages?.[0]?.timestamp ?? 0
return (
messages const newMessages = await receiveNewWakuMessages(lastTimestamp, this.pollInitTopic, this.waku)
?.filter((e): e is WakuMessage & { payload: Uint8Array } => !!e?.payload) const newPollInitMessages = decodeWakuMessages(newMessages, PollInit)
.map((msg) => PollInit.decode(msg.payload, msg.timestamp)) if (newPollInitMessages.length > 0) {
.filter((poll): poll is PollInitMsg => !!poll) ?? [] this.timedPollInitMessages = [...newPollInitMessages, ...this.timedPollInitMessages]
) }
return this.timedPollInitMessages
} }
public async sendTimedPollVote( public async sendTimedPollVote(
@ -92,13 +120,14 @@ class WakuVoting {
} }
private async getTimedPollsVotes() { private async getTimedPollsVotes() {
const messages = await this.waku?.store.queryHistory({ contentTopics: [this.timedPollVoteTopic] }) const lastTimestamp = this.timedPollVotesMessages?.[0]?.timestamp ?? 0
return (
messages const newMessages = await receiveNewWakuMessages(lastTimestamp, this.timedPollVoteTopic, this.waku)
?.filter((e): e is WakuMessage & { payload: Uint8Array } => !!e?.payload) const newVoteMessages = decodeWakuMessages(newMessages, TimedPollVote)
.map((msg) => TimedPollVote.decode(msg.payload, msg.timestamp)) if (newVoteMessages.length > 0) {
.filter((poll): poll is TimedPollVoteMsg => !!poll) ?? [] this.timedPollVotesMessages = [...newVoteMessages, ...this.timedPollVotesMessages]
) }
return this.timedPollVotesMessages
} }
public async getDetailedTimedPolls() { public async getDetailedTimedPolls() {

View File

@ -39,7 +39,7 @@ export function createSignMsgParams(message: Message) {
if (message.tokenAmount) { if (message.tokenAmount) {
msgParams.message = { ...msgParams.message, tokenAmount: message.tokenAmount.toString() } 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 return msgParams
} }

View File

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

View File

@ -33,10 +33,13 @@ export function encode(timedPollVote: TimedPollVoteMsg) {
} }
export function decode( export function decode(
payload: Uint8Array, payload: Uint8Array | undefined,
timestamp: Date | undefined, timestamp: Date | undefined,
recoverFunction?: ({ data, sig }: { data: any; sig: string }) => string recoverFunction?: ({ data, sig }: { data: any; sig: string }) => string
) { ) {
if (!payload) {
return undefined
}
try { try {
const msg = proto.TimedPollVote.decode(payload) const msg = proto.TimedPollVote.decode(payload)
if (!timestamp || timestamp.getTime() != msg.timestamp) { 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 [selectedAnswer, setSelectedAnswer] = useState(0)
const [tokenAmount, setTokenAmount] = useState(0) const [tokenAmount, setTokenAmount] = useState(0)
const [address, setAddress] = useState('') const [address, setAddress] = useState('')
const [userInVoters, setUserInVoters] = useState(false)
useEffect(() => { useEffect(() => {
signer.getAddress().then((e) => setAddress(e)) signer.getAddress().then((e) => setAddress(e))
}, [signer]) }, [signer])
useEffect(() => {
setUserInVoters(!!poll.votesMessages.find((vote) => vote.voter === address))
}, [poll])
return ( return (
<PollWrapper> <PollWrapper>
<PollTitle> <PollTitle>
@ -28,7 +32,7 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
<PollTypeWrapper>{poll.poll.pollType === PollType.WEIGHTED ? 'WEIGHTED' : 'NON WEIGHTED'}</PollTypeWrapper> <PollTypeWrapper>{poll.poll.pollType === PollType.WEIGHTED ? 'WEIGHTED' : 'NON WEIGHTED'}</PollTypeWrapper>
</PollTitle> </PollTitle>
<PollAnswersWrapper> <PollAnswersWrapper>
{!poll.votesMessages.find((vote) => vote.voter === address) && ( {!userInVoters && (
<div> <div>
<div onChange={(e) => setSelectedAnswer(Number.parseInt((e.target as any).value ?? 0))}> <div onChange={(e) => setSelectedAnswer(Number.parseInt((e.target as any).value ?? 0))}>
{poll.poll.answers.map((answer, idx) => { {poll.poll.answers.map((answer, idx) => {
@ -51,7 +55,7 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
)} )}
</div> </div>
)} )}
{poll.votesMessages.find((vote) => vote.voter === address) && ( {userInVoters && (
<div> <div>
Results Results
{poll.answers.map((answer, idx) => { {poll.answers.map((answer, idx) => {
@ -65,6 +69,7 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
</div> </div>
)} )}
</PollAnswersWrapper> </PollAnswersWrapper>
{!userInVoters && (
<button <button
onClick={() => { onClick={() => {
if (wakuVoting) { if (wakuVoting) {
@ -80,6 +85,7 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
{' '} {' '}
Vote Vote
</button> </button>
)}
</PollWrapper> </PollWrapper>
) )
} }

View File

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

View File

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