Skip to content

Commit 8b4221e

Browse files
committed
simplify and optimize activity writes - closes lichess-org#9820
1 parent d739780 commit 8b4221e

File tree

2 files changed

+91
-137
lines changed

2 files changed

+91
-137
lines changed

modules/activity/src/main/ActivityWriteApi.scala

+80-126
Original file line numberDiff line numberDiff line change
@@ -16,138 +16,81 @@ final class ActivityWriteApi(
1616
import activities._
1717
import model._
1818

19-
def game(game: Game): Funit = withColl { coll =>
20-
game.userIds
21-
.flatMap { userId =>
22-
for {
23-
pt <- game.perfType
24-
player <- game playerByUserId userId
25-
} yield for {
26-
a <- getOrCreate(userId)
27-
setGames = !game.isCorrespondence ?? $doc(
28-
ActivityFields.games -> a.games.orDefault
29-
.add(pt, Score.make(game wonBy player.color, RatingProg make player))
30-
)
31-
setCorres = game.hasCorrespondenceClock ?? $doc(
32-
ActivityFields.corres -> a.corres.orDefault.add(GameId(game.id), moved = false, ended = true)
33-
)
34-
setters = setGames ++ setCorres
35-
_ <- (!setters.isEmpty) ?? coll.update.one($id(a.id), $set(setters), upsert = true).void
36-
} yield ()
37-
}
38-
.sequenceFu
39-
.void
40-
}
41-
42-
def forumPost(post: lila.forum.Post): Funit = withColl { coll =>
19+
def game(game: Game): Funit =
20+
(for {
21+
userId <- game.userIds
22+
pt <- game.perfType
23+
player <- game playerByUserId userId
24+
} yield update(userId) { a =>
25+
val setGames = !game.isCorrespondence ?? $doc(
26+
ActivityFields.games -> a.games.orDefault
27+
.add(pt, Score.make(game wonBy player.color, RatingProg make player))
28+
)
29+
val setCorres = game.hasCorrespondenceClock ?? $doc(
30+
ActivityFields.corres -> a.corres.orDefault.add(GameId(game.id), moved = false, ended = true)
31+
)
32+
setGames ++ setCorres
33+
}).sequenceFu.void
34+
35+
def forumPost(post: lila.forum.Post): Funit =
4336
post.userId.filter(User.lichessId !=) ?? { userId =>
44-
getOrCreate(userId) flatMap { a =>
45-
coll.update
46-
.one(
47-
$id(a.id),
48-
$set(ActivityFields.forumPosts -> (~a.forumPosts + ForumPostId(post.id))),
49-
upsert = true
50-
)
51-
.void
37+
update(userId) { a =>
38+
$doc(ActivityFields.forumPosts -> (~a.forumPosts + ForumPostId(post.id)))
5239
}
5340
}
54-
}
5541

56-
def ublogPost(post: lila.ublog.UblogPost): Funit = withColl { coll =>
57-
getOrCreate(post.created.by) flatMap { a =>
58-
coll.update
59-
.one(
60-
$id(a.id),
61-
$set(ActivityFields.ublogPosts -> (~a.ublogPosts + UblogPostId(post.id.value))),
62-
upsert = true
63-
)
64-
.void
65-
}
42+
def ublogPost(post: lila.ublog.UblogPost): Funit = update(post.created.by) { a =>
43+
$doc(ActivityFields.ublogPosts -> (~a.ublogPosts + UblogPostId(post.id.value)))
6644
}
6745

68-
def puzzle(res: lila.puzzle.Puzzle.UserResult): Funit = withColl { coll =>
69-
getOrCreate(res.userId) flatMap { a =>
70-
coll.update
71-
.one(
72-
$id(a.id),
73-
$set(ActivityFields.puzzles -> {
74-
~a.puzzles + Score.make(
75-
res = res.result.win.some,
76-
rp = RatingProg(Rating(res.rating._1), Rating(res.rating._2)).some
77-
)
78-
}),
79-
upsert = true
80-
)
81-
.void
82-
}
46+
def puzzle(res: lila.puzzle.Puzzle.UserResult): Funit = update(res.userId) { a =>
47+
$doc(ActivityFields.puzzles -> {
48+
~a.puzzles + Score.make(
49+
res = res.result.win.some,
50+
rp = RatingProg(Rating(res.rating._1), Rating(res.rating._2)).some
51+
)
52+
})
8353
}
8454

85-
def storm(userId: User.ID, score: Int): Funit = withColl { coll =>
86-
getOrCreate(userId) flatMap { a =>
87-
coll.update
88-
.one(
89-
$id(a.id),
90-
$set(ActivityFields.storm -> { ~a.storm + score }),
91-
upsert = true
92-
)
93-
.void
94-
}
55+
def storm(userId: User.ID, score: Int): Funit = update(userId) { a =>
56+
$doc(ActivityFields.storm -> { ~a.storm + score })
9557
}
9658

97-
def racer(userId: User.ID, score: Int): Funit = withColl { coll =>
98-
getOrCreate(userId) flatMap { a =>
99-
coll.update
100-
.one(
101-
$id(a.id),
102-
$set(ActivityFields.racer -> { ~a.racer + score }),
103-
upsert = true
104-
)
105-
.void
106-
}
59+
def racer(userId: User.ID, score: Int): Funit = update(userId) { a =>
60+
$doc(ActivityFields.racer -> { ~a.racer + score })
10761
}
10862

109-
def streak(userId: User.ID, score: Int): Funit = withColl { coll =>
110-
getOrCreate(userId) flatMap { a =>
111-
coll.update
112-
.one(
113-
$id(a.id),
114-
$set(ActivityFields.streak -> { ~a.streak + score }),
115-
upsert = true
116-
)
117-
.void
118-
}
63+
def streak(userId: User.ID, score: Int): Funit = update(userId) { a =>
64+
$doc(ActivityFields.streak -> { ~a.streak + score })
11965
}
12066

121-
def learn(userId: User.ID, stage: String) =
122-
update(userId) { a =>
123-
a.copy(learn = Some(~a.learn + Learn.Stage(stage))).some
124-
}
67+
def learn(userId: User.ID, stage: String) = update(userId) { a =>
68+
$doc(ActivityFields.learn -> { ~a.learn + Learn.Stage(stage) })
69+
}
12570

126-
def practice(prog: lila.practice.PracticeProgress.OnComplete) =
127-
update(prog.userId) { a =>
128-
a.copy(practice = Some(~a.practice + prog.studyId)).some
129-
}
71+
def practice(prog: lila.practice.PracticeProgress.OnComplete) = update(prog.userId) { a =>
72+
$doc(ActivityFields.practice -> { ~a.practice + prog.studyId })
73+
}
13074

13175
def simul(simul: lila.simul.Simul) =
132-
simulParticipant(simul, simul.hostId) >>
133-
simul.pairings.map(_.player.user).map { simulParticipant(simul, _) }.sequenceFu.void
134-
135-
def corresMove(gameId: Game.ID, userId: User.ID) =
136-
update(userId) { a =>
137-
a.copy(corres = Some((~a.corres).add(GameId(gameId), moved = true, ended = false))).some
76+
lila.common.Future.applySequentially(simul.hostId :: simul.pairings.map(_.player.user)) {
77+
simulParticipant(simul, _)
13878
}
13979

140-
def plan(userId: User.ID, months: Int) =
141-
update(userId) { a =>
142-
a.copy(patron = Some(Patron(months))).some
143-
}
80+
def corresMove(gameId: Game.ID, userId: User.ID) = update(userId) { a =>
81+
$doc(ActivityFields.corres -> { (~a.corres).add(GameId(gameId), moved = true, ended = false) })
82+
}
83+
84+
def plan(userId: User.ID, months: Int) = update(userId) { a =>
85+
$doc(ActivityFields.patron -> Patron(months))
86+
}
14487

14588
def follow(from: User.ID, to: User.ID) =
14689
update(from) { a =>
147-
a.copy(follows = Some(~a.follows addOut to)).some
90+
$doc(ActivityFields.follows -> { ~a.follows addOut to })
14891
} >>
14992
update(to) { a =>
150-
a.copy(follows = Some(~a.follows addIn from)).some
93+
$doc(ActivityFields.follows -> { ~a.follows addIn from })
15194
}
15295

15396
def unfollowAll(from: User, following: Set[User.ID]) =
@@ -176,36 +119,47 @@ final class ActivityWriteApi(
176119
studyApi byId id flatMap {
177120
_.filter(_.isPublic) ?? { s =>
178121
update(s.ownerId) { a =>
179-
a.copy(studies = Some(~a.studies + s.id)).some
122+
$doc(ActivityFields.studies -> { ~a.studies + s.id })
180123
}
181124
}
182125
}
183126

184127
def team(id: String, userId: User.ID) =
185128
update(userId) { a =>
186-
a.copy(teams = Some(~a.teams + id)).some
129+
$doc(ActivityFields.teams -> { ~a.teams + id })
187130
}
188131

189132
def streamStart(userId: User.ID) =
190-
update(userId) { _.copy(stream = true).some }
133+
update(userId) { _ =>
134+
$doc(ActivityFields.stream -> true)
135+
}
191136

192137
def swiss(id: lila.swiss.Swiss.Id, ranking: lila.swiss.Ranking) =
193-
ranking.map { case (userId, rank) =>
194-
update(userId) { a => a.copy(swisses = Some(~a.swisses + SwissRank(id, rank))).some }
195-
}.sequenceFu
196-
197-
private def simulParticipant(simul: lila.simul.Simul, userId: String) =
198-
update(userId) { a =>
199-
a.copy(simuls = Some(~a.simuls + SimulId(simul.id))).some
138+
lila.common.Future.applySequentially(ranking.toList) { case (userId, rank) =>
139+
update(userId) { a =>
140+
$doc(ActivityFields.swisses -> { ~a.swisses + SwissRank(id, rank) })
141+
}
200142
}
201143

202-
private def get(userId: User.ID) = withColl(_.byId[Activity, Id](Id today userId))
203-
private def getOrCreate(userId: User.ID) = get(userId) map { _ | Activity.make(userId) }
204-
private def save(activity: Activity) = withColl(
205-
_.update.one($id(activity.id), activity, upsert = true).void
206-
)
207-
private def update(userId: User.ID)(f: Activity => Option[Activity]): Funit =
208-
getOrCreate(userId) flatMap { old =>
209-
f(old) ?? save
144+
private def simulParticipant(simul: lila.simul.Simul, userId: User.ID) = update(userId) { a =>
145+
$doc(ActivityFields.simuls -> { ~a.simuls + SimulId(simul.id) })
146+
}
147+
148+
private def update(userId: User.ID)(makeSetters: Activity => Bdoc): Funit =
149+
withColl { coll =>
150+
coll.byId[Activity, Id](Id today userId).dmap { _ | Activity.make(userId) } flatMap { activity =>
151+
val setters = makeSetters(activity)
152+
!setters.isEmpty ?? {
153+
coll.update
154+
.one($id(activity.id), $set(setters), upsert = true)
155+
.flatMap { res =>
156+
(res.upserted.nonEmpty ?? truncate(coll, activity.id.userId))
157+
}
158+
.void
159+
}
160+
}
210161
}
162+
163+
private def truncate(coll: Coll, userId: User.ID) = funit
164+
211165
}

modules/activity/src/main/BSONHandlers.scala

+11-11
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,24 @@ private object BSONHandlers {
9999
def writes(w: lila.db.BSON.Writer, r: Streak) = BSONDocument("r" -> r.runs, "s" -> r.score)
100100
}
101101

102-
implicit private lazy val learnHandler =
102+
implicit lazy val learnHandler =
103103
typedMapHandler[Learn.Stage, Int](Iso.string(Learn.Stage.apply, _.value))
104104
.as[Learn](Learn.apply, _.value)
105105

106-
implicit private lazy val practiceHandler =
106+
implicit lazy val practiceHandler =
107107
typedMapHandler[Study.Id, Int](Iso.string[Study.Id](Study.Id.apply, _.value))
108108
.as[Practice](Practice.apply, _.value)
109109

110-
implicit private lazy val simulIdHandler = BSONStringHandler.as[SimulId](SimulId.apply, _.value)
111-
implicit private lazy val simulsHandler =
110+
implicit lazy val simulIdHandler = BSONStringHandler.as[SimulId](SimulId.apply, _.value)
111+
implicit lazy val simulsHandler =
112112
isoHandler[Simuls, List[SimulId]]((s: Simuls) => s.value, Simuls.apply _)
113113

114-
implicit lazy val corresHandler = Macros.handler[Corres]
115-
implicit private lazy val patronHandler = BSONIntegerHandler.as[Patron](Patron.apply, _.months)
114+
implicit lazy val corresHandler = Macros.handler[Corres]
115+
implicit lazy val patronHandler = BSONIntegerHandler.as[Patron](Patron.apply, _.months)
116116

117-
implicit private lazy val followListHandler = Macros.handler[FollowList]
117+
implicit lazy val followListHandler = Macros.handler[FollowList]
118118

119-
implicit private lazy val followsHandler = new lila.db.BSON[Follows] {
119+
implicit lazy val followsHandler = new lila.db.BSON[Follows] {
120120
def reads(r: lila.db.BSON.Reader) =
121121
Follows(
122122
in = r.getO[FollowList]("i").filterNot(_.isEmpty),
@@ -129,16 +129,16 @@ private object BSONHandlers {
129129
)
130130
}
131131

132-
implicit private lazy val studiesHandler =
132+
implicit lazy val studiesHandler =
133133
isoHandler[Studies, List[Study.Id]]((s: Studies) => s.value, Studies.apply _)
134-
implicit private lazy val teamsHandler =
134+
implicit lazy val teamsHandler =
135135
isoHandler[Teams, List[String]]((s: Teams) => s.value, Teams.apply _)
136136

137137
implicit lazy val swissRankHandler = new lila.db.BSON[SwissRank] {
138138
def reads(r: lila.db.BSON.Reader) = SwissRank(Swiss.Id(r.str("i")), r.intD("r"))
139139
def writes(w: lila.db.BSON.Writer, s: SwissRank) = BSONDocument("i" -> s.id, "r" -> s.rank)
140140
}
141-
implicit private lazy val swissesHandler =
141+
implicit lazy val swissesHandler =
142142
isoHandler[Swisses, List[SwissRank]]((s: Swisses) => s.value, Swisses.apply _)
143143

144144
object ActivityFields {

0 commit comments

Comments
 (0)