Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.1.1.: Video relevance fixes #350

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
# 4.1.1

## Affected components:
- Processor (non-breaking changes)
- GraphQL server (non-breaking changes)

## Bug Fixes:
- Fixed video relevance calculation algorithm by:
- Ignoring `publishedBeforeJoystream` date if it's in the future,
- Fixing a bug with Joystream/YT creation weight formula,
- Introduced an age limit of 365 days to prevent negative relevance score
- Fixed ineffective `videoRelevanceManager` calls in GraphQL server due to `videoRelevanceManager` not being turned on.
- Fixed a bug causing both GraphQL server and Processor services to run video relevance and language update loops (which only processor should be running)
- Fixed imports in GraphQl server and Processor by moving shared utilities to root `utils` directory to prevent mix-ups.


# 4.1.0

## Affected components:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "orion",
"version": "4.1.0",
"version": "4.1.1",
"engines": {
"node": ">=16"
},
Expand Down
8 changes: 2 additions & 6 deletions src/mappings/content/commentsAndReactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,9 @@ import {
commentCountersManager,
videoRelevanceManager,
} from '../utils'
import {
getAccountForMember,
getChannelOwnerMemberByChannelId,
memberHandleById,
parseVideoTitle,
} from './utils'
import { getAccountForMember, getChannelOwnerMemberByChannelId, memberHandleById } from './utils'
import { addNotification } from '../../utils/notification'
import { parseVideoTitle } from '../../utils/notification/helpers'

function parseVideoReaction(reaction: ReactVideo.Reaction): VideoReactionOptions {
const protobufReactionToGraphqlReaction = {
Expand Down
2 changes: 1 addition & 1 deletion src/mappings/content/nft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ import {
notifyBiddersOnAuctionCompletion,
notifyChannelFollowers,
parseContentActor,
parseVideoTitle,
processNft,
} from './utils'
import { parseVideoTitle } from '../../utils/notification/helpers'

export async function processOpenAuctionStartedEvent({
overlay,
Expand Down
12 changes: 1 addition & 11 deletions src/mappings/content/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import { criticalError } from '../../utils/misc'
import { addNotification } from '../../utils/notification'
import { EntityManagerOverlay, Flat } from '../../utils/overlay'
import { addNftActivity, addNftHistoryEntry, genericEventFields, invalidMetadata } from '../utils'
import { parseChannelTitle, parseVideoTitle } from '../../utils/notification/helpers'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AsDecoded<MetaClass> = MetaClass extends { create: (props?: infer I) => any }
Expand Down Expand Up @@ -887,14 +888,6 @@ export function increaseChannelCumulativeRevenue(channel: Flat<Channel>, amount:
channel.cumulativeRevenue = (channel.cumulativeRevenue || 0n) + amount
}

export function parseChannelTitle(channel: Flat<Channel>): string {
return channel.title || FALLBACK_CHANNEL_TITLE
}

export function parseVideoTitle(video: Flat<Video>): string {
return video.title || FALLBACK_VIDEO_TITLE
}

export async function memberHandleById(
overlay: EntityManagerOverlay,
memberId: string
Expand All @@ -908,6 +901,3 @@ export async function getChannelTitleById(overlay: EntityManagerOverlay, channel
const channel = await overlay.getRepository(Channel).getByIdOrFail(channelId)
return parseChannelTitle(channel)
}

export const FALLBACK_CHANNEL_TITLE = '??'
export const FALLBACK_VIDEO_TITLE = '??'
3 changes: 1 addition & 2 deletions src/mappings/content/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ import {
deleteVideo,
encodeAssets,
notifyChannelFollowers,
parseChannelTitle,
parseContentActor,
parseVideoTitle,
processAppActionMetadata,
processNft,
} from './utils'
import { parseChannelTitle, parseVideoTitle } from '../../utils/notification/helpers'

export async function processVideoCreatedEvent({
overlay,
Expand Down
3 changes: 2 additions & 1 deletion src/mappings/token/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
import { EventHandlerContext } from '../../utils/events'
import { criticalError } from '../../utils/misc'
import { addNotification } from '../../utils/notification'
import { getChannelOwnerAccount, notifyChannelFollowers, parseChannelTitle } from '../content/utils'
import { getChannelOwnerAccount, notifyChannelFollowers } from '../content/utils'
import { deserializeMetadata, genericEventFields } from '../utils'
import {
VestingScheduleData,
Expand All @@ -60,6 +60,7 @@
processTokenMetadata,
processValidatedTransfers,
} from './utils'
import { parseChannelTitle } from '../../utils/notification/helpers'

export async function processTokenIssuedEvent({
overlay,
Expand All @@ -78,7 +79,7 @@
},
}: EventHandlerContext<'ProjectToken.TokenIssued'>) {
// create token
const totalSupply = initialAllocation.reduce((acc, [_, allocation]) => {

Check warning on line 82 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

'_' is defined but never used
return acc + allocation.amount
}, BigInt(0))

Expand Down Expand Up @@ -398,7 +399,7 @@
buyerAccount.totalAmount += crtMinted
}

const activeAmm = await overlay.getRepository(AmmCurve).getByIdOrFail(token.currentAmmSaleId!)

Check warning on line 402 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion

activeAmm.mintedByAmm += crtMinted
const tx = overlay.getRepository(AmmTransaction).new({
Expand Down Expand Up @@ -458,7 +459,7 @@
.getOneByRelationOrFail('tokenId', tokenId.toString())
const channel = await overlay.getRepository(Channel).getByIdOrFail(tokenChannel.channelId)
token.totalSupply -= crtBurned
const activeAmm = await overlay.getRepository(AmmCurve).getByIdOrFail(token.currentAmmSaleId!)

Check warning on line 462 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion
const ammId = activeAmm.id

const sellerAccount = await getTokenAccountByMemberByTokenOrFail(overlay, memberId, tokenId)
Expand Down Expand Up @@ -531,7 +532,7 @@
}

const token = await overlay.getRepository(CreatorToken).getByIdOrFail(tokenId.toString())
const sale = await overlay.getRepository(Sale).getByIdOrFail(token.currentSaleId!)

Check warning on line 535 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion
sale.tokensSold += amountPurchased

const tx = overlay.getRepository(SaleTransaction).new({
Expand Down Expand Up @@ -591,7 +592,7 @@
},
}: EventHandlerContext<'ProjectToken.UpcomingTokenSaleUpdated'>) {
const token = await overlay.getRepository(CreatorToken).getByIdOrFail(tokenId.toString())
const sale = await overlay.getRepository(Sale).getByIdOrFail(token.currentSaleId!)

Check warning on line 595 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion

if (newStart) {
sale.startBlock = newStart
Expand Down Expand Up @@ -710,13 +711,13 @@
export async function processAmmDeactivatedEvent({
overlay,
event: {
asV2002: [tokenId, , burnedAmount],

Check warning on line 714 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

'burnedAmount' is defined but never used
},
}: EventHandlerContext<'ProjectToken.AmmDeactivated'>) {
const token = await overlay.getRepository(CreatorToken).getByIdOrFail(tokenId.toString())
token.status = TokenStatus.IDLE

const activeAmm = await overlay.getRepository(AmmCurve).getByIdOrFail(token.currentAmmSaleId!)

Check warning on line 720 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion
activeAmm.finalized = true

token.currentAmmSaleId = null
Expand Down Expand Up @@ -755,12 +756,12 @@
},
}: EventHandlerContext<'ProjectToken.TokenSaleFinalized'>) {
const token = await overlay.getRepository(CreatorToken).getByIdOrFail(tokenId.toString())
const sale = await overlay.getRepository(Sale).getByIdOrFail(token.currentSaleId!)

Check warning on line 759 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion
sale.finalized = true

const sourceAccount = await overlay
.getRepository(TokenAccount)
.getByIdOrFail(sale.fundsSourceAccountId!)

Check warning on line 764 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion
sourceAccount.totalAmount += quantityLeft

token.status = TokenStatus.IDLE
Expand Down Expand Up @@ -796,7 +797,7 @@
const token = await overlay.getRepository(CreatorToken).getByIdOrFail(tokenId.toString())
const revenueShare = await overlay
.getRepository(RevenueShare)
.getByIdOrFail(token.currentRevenueShareId!)

Check warning on line 800 in src/mappings/token/index.ts

View workflow job for this annotation

GitHub Actions / Local build, linting and formatting (ubuntu-latest, 18.x)

Forbidden non-null assertion
revenueShare.finalized = true
token.currentRevenueShareId = null
}
Expand Down
9 changes: 0 additions & 9 deletions src/mappings/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,6 @@ import { OrionVideoLanguageManager } from '../utils/OrionVideoLanguageManager'
export const orionVideoLanguageManager = new OrionVideoLanguageManager()
export const commentCountersManager = new CommentCountersManager()
export const videoRelevanceManager = new VideoRelevanceManager()
// eslint-disable-next-line no-void
void orionVideoLanguageManager.init(
1000 * 60 * 5 // 5 mins
)
// eslint-disable-next-line no-void
void videoRelevanceManager.init({
fullUpdateLoopTime: 1000 * 60 * 60 * 12, // 12 hrs
scheduledUpdateLoopTime: 1000 * 60 * 10, // 10 mins
})
export const JOYSTREAM_SS58_PREFIX = 126

export function bytesToString(b: Uint8Array): string {
Expand Down
25 changes: 24 additions & 1 deletion src/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ import {
processUpcomingTokenSaleUpdatedEvent,
processUserParticipatedInSplitEvent,
} from './mappings/token'
import { commentCountersManager, videoRelevanceManager } from './mappings/utils'
import {
commentCountersManager,
videoRelevanceManager,
orionVideoLanguageManager,
} from './mappings/utils'
import { Event } from './types/support'
import { EventHandler, EventInstance, EventNames, eventConstructors } from './utils/events'
import { assertAssignable } from './utils/misc'
Expand Down Expand Up @@ -363,6 +367,25 @@ async function afterDbUpdate(em: EntityManager) {
}

const offchainState = new OffchainState()

// Initialize update intervals
orionVideoLanguageManager
.init(
1000 * 60 * 5 // 5 mins
)
.catch((e) => {
throw new Error(`Failed to initialize Orion video language manager: ${e.toString()}`)
})

videoRelevanceManager
.init({
fullUpdateLoopTime: 1000 * 60 * 60 * 12, // 12 hrs
scheduledUpdateLoopTime: 1000 * 60 * 10, // 10 mins
})
.catch((e) => {
throw new Error(`Failed to initialize Orion video relevance manager: ${e.toString()}`)
})

let exportBlockNumber: number

processor.run(new TypeormDatabase({ isolationLevel: 'READ COMMITTED' }), async (ctx) => {
Expand Down
8 changes: 4 additions & 4 deletions src/server-extension/resolvers/AdminResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { GraphQLResolveInfo } from 'graphql'
import 'reflect-metadata'
import { Args, Ctx, Info, Int, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'
import { EntityManager, In, Not, UpdateResult } from 'typeorm'
import { parseVideoTitle } from '../../../mappings/content/utils'
import { videoRelevanceManager } from '../../../mappings/utils'
import {
Account,
Channel,
Expand Down Expand Up @@ -76,6 +74,8 @@ import {
VideoWeights,
} from './types'
import { processCommentsCensorshipStatusUpdate } from './utils'
import { recalculateAllVideosRelevance } from '../../utils'
import { parseVideoTitle } from '../../../utils/notification/helpers'

@Resolver()
export class AdminResolver {
Expand Down Expand Up @@ -161,7 +161,7 @@ export class AdminResolver {
],
em
)
await videoRelevanceManager.updateVideoRelevanceValue(em, true)
await recalculateAllVideosRelevance(em)
return { isApplied: true }
}

Expand Down Expand Up @@ -222,7 +222,7 @@ export class AdminResolver {
}
)

await videoRelevanceManager.updateVideoRelevanceValue(em, true)
await recalculateAllVideosRelevance(em)

// Push the result into the results array
results.push({
Expand Down
11 changes: 5 additions & 6 deletions src/server-extension/resolvers/AdminResolver/utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { EntityManager, In } from 'typeorm'
import { CommentCountersManager } from '../../../utils/CommentsCountersManager'
import { Comment } from '../../../model'
import { commentCountersManager } from '../../utils'

export async function processCommentsCensorshipStatusUpdate(em: EntityManager, ids: string[]) {
const manager = new CommentCountersManager()
const comments = await em.getRepository(Comment).find({ where: { id: In(ids) } })
comments.forEach((c) => {
manager.scheduleRecalcForComment(c.parentCommentId)
manager.scheduleRecalcForVideo(c.videoId)
commentCountersManager.scheduleRecalcForComment(c.parentCommentId)
commentCountersManager.scheduleRecalcForVideo(c.videoId)
})
await manager.updateVideoCommentsCounters(em)
await manager.updateParentRepliesCounters(em)
await commentCountersManager.updateVideoCommentsCounters(em)
await commentCountersManager.updateParentRepliesCounters(em)
}
4 changes: 2 additions & 2 deletions src/server-extension/resolvers/ChannelsResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ import { uniqueId } from '../../../utils/crypto'
import { AccountOnly, OperatorOnly, UserOnly } from '../middleware'
import { addNotification } from '../../../utils/notification'
import { assertNotNull } from '@subsquid/substrate-processor'
import { FALLBACK_CHANNEL_TITLE } from '../../../mappings/content/utils'
import pLimit from 'p-limit'
import { parseChannelTitle } from '../../../utils/notification/helpers'

@Resolver()
export class ChannelsResolver {
Expand Down Expand Up @@ -463,7 +463,7 @@ export const excludeChannelService = async (
em,
account,
new MemberRecipient({ membership: channelOwnerMemberId }),
new ChannelExcluded({ channelTitle: channel.title ?? FALLBACK_CHANNEL_TITLE })
new ChannelExcluded({ channelTitle: parseChannelTitle(channel) })
)
}

Expand Down
4 changes: 2 additions & 2 deletions src/server-extension/resolvers/VideosResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import { isObject } from 'lodash'
import 'reflect-metadata'
import { Arg, Args, Ctx, Info, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'
import { EntityManager, In, MoreThan } from 'typeorm'
import { parseVideoTitle } from '../../../mappings/content/utils'
import { videoRelevanceManager } from '../../../mappings/utils'
import {
Account,
ChannelRecipient,
Expand Down Expand Up @@ -53,6 +51,8 @@ import {
SetOrUnsetPublicFeedResult,
VideoReportInfo,
} from './types'
import { videoRelevanceManager } from '../../utils'
import { parseVideoTitle } from '../../../utils/notification/helpers'

@Resolver()
export class VideosResolver {
Expand Down
12 changes: 12 additions & 0 deletions src/server-extension/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { EntityManager } from 'typeorm'
import { CommentCountersManager } from '../utils/CommentsCountersManager'
import { VideoRelevanceManager } from '../utils/VideoRelevanceManager'

export const commentCountersManager = new CommentCountersManager()
export const videoRelevanceManager = new VideoRelevanceManager()

videoRelevanceManager.turnOnVideoRelevanceManager()

export async function recalculateAllVideosRelevance(em: EntityManager) {
return videoRelevanceManager.updateVideoRelevanceValue(em, true)
}
44 changes: 30 additions & 14 deletions src/utils/VideoRelevanceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { EntityManager } from 'typeorm'
import { config, ConfigVariable } from './config'
import { globalEm } from './globalEm'

// constant used to parse seconds from creation
export const NEWNESS_SECONDS_DIVIDER = 60 * 60 * 24
export const SECONDS_PER_DAY = 60 * 60 * 24

type VideoRelevanceManagerLoops = {
fullUpdateLoopTime: number
Expand Down Expand Up @@ -67,23 +66,40 @@ export class VideoRelevanceManager {
defaultChannelWeight,
] = await config.get(ConfigVariable.RelevanceWeights, em)
const channelWeight = defaultChannelWeight ?? 1
const wtEpoch = `((
extract(epoch from video.created_at)*${joystreamTimestampWeight} +
COALESCE(extract(epoch from video.published_before_joystream), extract(epoch from video.created_at))*${ytTimestampWeight}
) / ${joystreamTimestampWeight} + ${ytTimestampWeight})`
const weightedEpoch = `
(
(
extract(epoch from video.created_at) * ${joystreamTimestampWeight} +
CASE
WHEN (
video.published_before_joystream IS NOT NULL
AND video.published_before_joystream < now()
) THEN extract(epoch from video.published_before_joystream)
ELSE extract(epoch from video.created_at)
END * ${ytTimestampWeight}
) / ${joystreamTimestampWeight + ytTimestampWeight}
)`
const weightedMeanVideoAgeDays = `
(
(extract(epoch FROM now()) - ${weightedEpoch})
/ ${SECONDS_PER_DAY}
)`

await em.query(`
WITH videos_with_weight AS (
SELECT
video.id as videoId,
channel.id as channelId,
(ROUND((
(extract(epoch FROM date_trunc('day', now() at time zone 'UTC')) - ${wtEpoch})
/ ${NEWNESS_SECONDS_DIVIDER} * ${newnessWeight * -1}
+ (views_num * ${viewsWeight})
+ (comments_count * ${commentsWeight})
+ (reactions_count * ${reactionsWeight}))
* COALESCE(channel.channel_weight, ${channelWeight}), 2)) as videoRelevance
ROUND(
(
365 * ${newnessWeight}
- LEAST(${weightedMeanVideoAgeDays}, 365) * ${newnessWeight}
+ (views_num * ${viewsWeight})
+ (comments_count * ${commentsWeight})
+ (reactions_count * ${reactionsWeight})
) * COALESCE(channel.channel_weight, ${channelWeight}),
2
) as videoRelevance
FROM
video
INNER JOIN channel ON video.channel_id = channel.id
Expand Down Expand Up @@ -129,7 +145,7 @@ export class VideoRelevanceManager {
SET
video_relevance = CASE
WHEN ranked_videos.rank = 1 THEN ranked_videos.maxChannelRelevance
ELSE 1
ELSE 0
END
FROM
ranked_videos
Expand Down
16 changes: 14 additions & 2 deletions src/utils/notification/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Flat } from 'lodash'
import { EntityManager } from 'typeorm'
import {
Account,
AccountNotificationPreferences,
Channel,
Event,
NextEntityId,
Notification,
Expand All @@ -11,11 +11,12 @@ import {
NotificationType,
RecipientType,
Unread,
Video,
} from '../../model'
import { getCurrentBlockHeight } from '../blockHeight'
import { uniqueId } from '../crypto'
import { getNextIdForEntity } from '../nextEntityId'
import { EntityManagerOverlay } from '../overlay'
import { EntityManagerOverlay, Flat } from '../overlay'

export const RUNTIME_NOTIFICATION_ID_TAG = 'RuntimeNotification'
export const OFFCHAIN_NOTIFICATION_ID_TAG = 'OffchainNotification'
Expand Down Expand Up @@ -339,3 +340,14 @@ const chunkFromEnd = (str: string, interval: number): string[] =>
)
const roundDecPart = (decPart: string, fractionDigits: number): string =>
Number(`.${decPart}`).toFixed(fractionDigits).slice(1)

export function parseChannelTitle(channel: Flat<Channel>): string {
return channel.title || FALLBACK_CHANNEL_TITLE
}

export function parseVideoTitle(video: Flat<Video>): string {
return video.title || FALLBACK_VIDEO_TITLE
}

export const FALLBACK_CHANNEL_TITLE = '??'
export const FALLBACK_VIDEO_TITLE = '??'
Loading