mgroves / realworld-aspnet-couchbase

Conduit implementation: ASP.NET Core + Couchbase
https://realworld.io
MIT License
12 stars 4 forks source link

Endpoint: Follow user #11

Open mgroves opened 1 year ago

mgroves commented 1 year ago

POST /api/profiles/:username/follow

Authentication required, returns a Profile

No additional parameters required

mgroves commented 1 year ago

My plan: a single document containing a list of usernames being followed.

Something like

key "jake::following"
{
"following" : ["username1", "username2", . . .] 
}

(Although, the document can probably be an array instead of an object with a named array in it (or maybe a Map, List, Queue, Set via Data Structures))

Couchbase max document size is 20mb. BUT according to ChatGPT, this could accommodate over 1.1 million follows, assuming usernames are 15 characters on average:

See https://chat.openai.com/share/adac6ff1-1498-4102-8959-b6cc7a40f925

The current constraint is 100 characters. If EVERY user had a 100 character username, the limit would be 194,174 (again per ChatGPT).

So I don't think doc size is a problem, unless other data needs to be added to the follow (date/time, reason, notes, whatever else). Twitter, in fact, puts a cap of 5000 follows for most people. However, I may want to reevaluate the max size of username anyway (Twitter restricts to 15 characters, I think).

I think the more interesting issue would be checking this document to see if a give username is in it. I'm hoping that the aforementioned Data Structures will provide that efficiency (via the subdocument API).

mgroves commented 1 year ago

Re: Data Structures, it might be a good idea to try BenchmarkDotNet, to see the performance impact, as well as compare/contrast the various data structures.

mgroves commented 1 year ago

I ran some benchmarks on various ways to interact with a "following" document:


BenchmarkDotNet v0.13.6, Windows 10 (10.0.19045.3208/22H2/2022Update)
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET SDK 7.0.304
  [Host]     : .NET 7.0.9 (7.0.923.32018), X64 RyuJIT AVX2
  Job-UQZKSN : .NET 7.0.9 (7.0.923.32018), X64 RyuJIT AVX2
IterationCount=50  
Method MeanErrorStdDevMedian
FindInList767.9 μs33.89 μs68.46 μs751.6 μs
FindInSet777.2 μs58.50 μs118.17 μs764.5 μs
FindInArray758.2 μs11.72 μs22.59 μs753.4 μs
AddToSet26,175.7 μs8,164.13 μs16,491.95 μs19,447.9 μs
AddToList4,813.2 μs769.36 μs1,554.14 μs4,635.8 μs
AddToArrayOptimistic5,120.1 μs813.25 μs1,642.81 μs5,182.3 μs
AddToArrayPessimistic12,774.7 μs1,290.59 μs2,607.05 μs12,890.9 μs

I'm leaning towards AddToArrayOptimistic, but List might work too. 🤔

mgroves commented 1 year ago

Set is the ideal choice, since it enforces uniqueness. However, you can see that AddToSet is the slowest (26ms isn't terribly slow, so it might be okay).

List is pretty fast, but AddToList as I wrote it does not do any locking, so there is a chance of race conditions.

AddToArrayOptimistic uses optimistic locking; it doesn't use any of the built in data structures. Of course, with optimistic locking, cas mismatch is possible, so that has to be accounted for in code. But I think optimistic locking in this situation in reality will almost never have a cas mismatch.