How utorrent implements the "vote" system over the DHT? - dht

Does anyone know how utorrent implements the "vote" system over the DHT?
I looked online but there is next to nothing about it.
Some information:
http://lists.ibiblio.org/pipermail/bittorrent/2011-January/002413.html
http://arxiv.org/ftp/arxiv/papers/1305/1305.0711.pdf
You get a message something like this:
{ a:
{ id: '907050177fb05376db71ce066663819503eb0a9a',
target: '546d6d0e5aacfae66299f26a300a428c497b23b5',
token: '5b3f3de3d4f5ba42721dac14b2076dc64bc829da',
vote: 0 },
q: 'vote',
t: '+=\u0000\u0000',
v: 'UT??',
y: 'q' }
The only thing I dont get it the target They say its sha1(info-hash + "rating") but i dont know. So the rating 1-5 and 0 been the query for votes. So if a infohash has 5 votes ranging from 1 to 5 would you receive 5 responses?
Has anyone played around with this?

Related

realtime database: next N events

I have a model for events. Events all have a start time
orgs: {
"$org": {
events:
"$event_key": {
...
details: {
start
I want to fetch the next 3 upcoming events:
i.e get all events that have a details.start > now, sorted asc, limit 3
I thought I could write something like this:
firebase
.database()
.ref('/orgs/foo')
.orderByChild('details/start')
.startAt(Date.now())
.limitToFirst(5)
.once('value', x => console.log(x.val()))
But I currently get an empty value. Is this the right approach? I am not 100% sure that I am using orderByChild correctly here.
oi. Forgot events. This did the trick:
firebase
.database()
.ref('/orgs/foo/events')
.orderByChild('details/start')
.startAt(Date.now())
.limitToFirst(3)
.once('value', (snap) => snap.forEach((child) => console.log(child.val().details.start)));

Rails way to match two users in real time

I'm developing a Rails application as a backend for a mobile app, via a JSON API. I've modeled pretty much everything, but there's one (core) process that needs to be designed and I'm not finding a clear way to implement it.
Besides from other features, the app must match two users that meet a certain conditions, mainly geographical. For the sake of the question and to simplify, let's say it needs to match users that are close to each other AND that are currently searching for a match (it's a synchronous experience), e.g.:
User A hits "Search partners" and a loading screen appears
User B hits "Search partners" and a loading screen appears
The users are, let's say, 5km apart. The experience should be:
They both see the loading screen for 5 seconds, while "the system" is looking for matches nearby (3km). After 5 seconds, it broadens the radius to 6km and it matches the two users. The two of them should navigate to the "Found a match" screen.
My main issue here is how to model this "looking for a match" status in Rails. I've thought of creating a table and model including a reference to the user and his position. But then I can't figure out how to deal with the "match query" without falling into a master-slave situation.
Basically, the ideal situation would be one in which both user's apps were in a kind of idle status and the backend, in case of a match, could notify them both, but in that case, the process in the backend should've to be not request-based but maybe a worker...I'm using Postgres with Postgis, so the storage of the user's position is possible, but not sure if maybe Redis would be a better choice, given the amount of changing rows...
I'm aware I'm being quite vague with my question, but it's really a matter of what approach to take, more than a code-level solution.
Thank you so much!
Sidekiq + WebSockets + psql should do just fine. To avoid master-slave situation match making should be based on invites between two looking for match users. The solution is pretty simple: when a user connects via a websocket to your rails app it starts FindMatchJob. The job checks if any other user in 5km distance range has invited us and accepts the first invites. Otherwise, it invites other users withing given range and shedules itself again with 1 second delay. I added some code as a proof of concept but it is not bulletproof in terms of concurrency issues. Also I simplified range expansion because I am lazy:)
class FindMatchJob < ApplicationJob
def perform(user, range: 5)
return unless user.looking_for_a_match?
invites = find_pending_invites(user, range)
return accept_invite(invite.first) if invites.any?
users_in_range = find_users_in_range(user, range)
users_in_range.each do |other_user|
Invite.create!(
inviting: user,
invited: other_user,
distance: other_user.distance_from(user)
)
end
self.class.set(wait: 1.second).perform_later(user, range: range + 1)
end
private
def find_pending_invites(user)
Invite.where('distance <= ?', distance).find_by(invited: user)
end
def accept_invite(invite)
notify_users(invite)
clear_other_invites(invite)
end
def find_users_in_range(user, range)
# somehow find those users
end
def notify_users(invite)
# Implement logic to notify users about a match via websockets
end
def clear_other_invites(invite)
Invite.where(
inviting: match.inviting
).or(
invited: match.inviting
).or(
inviting: match.invited
).or(
invited: match.invited
).delete_all
end
end
One more note. You may want to consider another tech stack to handle this issue. Rails are not the best in terms of concurrency. I would try with GoLang, it would be much better here.
Disclaimer: I have no prior experience of the feature you have in mind. But, I would do something like the following.
Assumptions:
All code below makes use of Postgis
PartnersLocation model has a t.st_point :geolocation, geographic: true attribute. Reference
Possible Solution:
Client-side connects to the Rails backend through ActionCable (websocket)
Example:
// JS
// when "Search Partner" is clicked, perform the following:
var currentRadius = 5;
// let XXX.XX and YYY.YY below to be the "current" location
// Request:
// POST /partners_locations.json
// { partners_location: { radius: 5, latitude: XXX.XX, longitude: YYY.YY } }
// Response:
// 201 Created
// { id: 14, user_id: 6, radius: 5, latitude: XXX.XX, longitude: YYY.YY }
// Using the `partners_location` ID above ^
App.cable.subscriptions.create(
{ channel: 'PartnersLocationsSearchChannel', id: 14 },
{
connected: function() {
this.search()
// search every 5 seconds, and incrementally increase the radius
setInterval(this.search, 5000)
},
// this function will be triggered when there is a match
received: function(data) {
console.log(data)
// i.e. will output: { id: 22, user_id: 7, latitude: XXX.XX, longitude: YYY.YY }
}
search: function() {
this.perform('search', { radius: currentRadius })
currentRadius += 1
}
}
)
Back-end side would be something like:
app/controllers/partners_locations_controller.rb:
class PartnersLocationsController < ApplicationController
def create
#partners_location = PartnersLocation.new(partners_location_params)
#partners_location.user = current_user
if #partners_location.save
render #partners_location, status: :created, location: #partners_location
else
render json: #partners_location.errors, status: :unprocessable_entity
end
end
private
def partners_location_params
params.require(:partners_location).permit(:radius, :latitude, :longitude)
end
end
app/channels/application_cable/partners_locations_search_channel.rb:
class PartnersLocationsSearchChannel < ApplicationCable::Channel
def subscribed
#current_partners_location = PartnersLocation.find(params[:id])
stream_for #current_partners_location
end
def unsubscribed
#current_partners_location.destroy
end
def search(data)
radius = data.fetch('radius')
#current_partners_location.update!(radius: radius)
partners_locations = PartnersLocation.where.not(
id: #current_partners_location.id
).where(
# TODO: update this `where` to do a Postgis two-circle (via radius) intersection query to get all the `partners_locations` of which their "radiuses" have already been taken accounted for.
# ^ haven't done this "circle-intersect" before, but probably the following will help: https://gis.stackexchange.com/questions/166685/postgis-intersect-two-circles-each-circle-must-be-built-from-long-lat-degrees?rq=1
)
partners_locations.each do |partners_location|
PartnersLocationsSearchChannel.broadcast_to(
partners_location,
#current_partners_location.as_json
)
PartnersLocationsSearchChannel.broadcast_to(
#current_partners_location,
partners_location.as_json
)
end
end
end
Code above still needs to be tweaked:
just update any of the code above to use JSON API instead
update the Client-side accordingly (it may not be JS). i.e. you can use:
https://github.com/danielrhodes/Swift-ActionCableClient
https://github.com/hosopy/actioncable-client-java
partners_locations#create still needs to be tweaked to save both latitude and longitude into the geolocation attribute
current_partner_location.as_json above still needs to be tweaked to return latitude and longitude instead of the attribute geolocation
def search above needs to be updated: the where Postgis 2-circle- intersect condition. I honestly don't know how to approach this. If anyone, please let me know. This is the closest I could find on the web
JS code above still needs to be tweaked to gracefully handle errors in connection, and also to stop the setInterval after successfully matching.
JS code above still needs to be tweaked to "unsubscribe" from the PartnersLocationsSearchChannel once the "loading-screen" has been closed, or when already found a "match", or something like that (depends on your requirements)

Show authorizations according to situations

I have Employee and this have authorizations. How to show only authorizations with situation 1 and 6.
this is my code:
if employee.authorizations.situation == 1
(...)
elsif employee.authorizations.situation == 6
(...)
But, ever return false, but I have in my database 3 authorizations situation 1, and 2 authorizations situation 6.
In console, when I enter employee.authorizations.first.situation == 1, retunr true. Same thing for situation 6.
What is my error? Thanks!
If you want to filter a collection by some property:
employee.authorizations.where(situation: 1)
This is the complete answer
employee.authorizations.where(situation: 1).exist?

Internally, how does relay cache pagination for subqueries?

Let's say I have a blog:
1 blog has many posts
1 post has many likers
1 post has many comments
1 comment has many likers
On the front page of my blog, I'd like to show the top 10 posts. For each post, I'd like to show the 10 most recent likers, the 5 most recent comments, and the 10 most recent people who liked each comment. (Numbers aren't important, I'm just setting up something similar to facebook).
So my query might look something like this:
query getPosts(
$postCount: Int,
$likersCount: Int,
$commentCount: Int,
$commentCursor: ID,
$commentLikersCount: Int) {
recentPosts(first: $postCount) {
id,
title,
body,
likers(first: $likersCount) {
id,
name
},
comments(first: $commentCount, after: $commentCursor) {
id,
title,
body,
likers(first: $commentLikersCount) {
id,
name
},
}
}
}
If I resubmit this query with a new $commentCursor to load more comments, how does relay cache the data so it knows to grab everything else locally? I get the basic graph architecture of the store, but for nested things like this I get confused in the debugger.
Normally you would extend the paginated range by changing the first: $commentCount argument, and you wouldn't use the after argument. Just say you change $commentCount from 5 to 10, Relay knows to get the the items at positions 6 through 10, as it already has the ones from 1 through 5. Specifically, you'll see it issue an first: 5, after: <cursor> query, where <cursor> is the cursor of the 5th comment (Relay will automatically get cursors for every edge in the connection for this purpose, even if you don't ask for them explicitly).

Find and modify, fetch the data, process it and save- Mongoid

I am using Mongoid in my Rails application and found i can use find_and_modify command to update a document as soon as find operation succeeds.
consider a collection User below document structure
name
points
so the documents are saved like
{ "_id" : "51a7420eb09de918204883c4", "name" : "balan", "points" : 1727 }
now how do i update the points count as soon as i fetch the record, is there any way to do like below
User.where(id: "51a7420eb09de918204883c4").find_and_modify( "$set" => { points: points + 1 } )
i.e., the system should fetch the stored points and increment it by 1 and save it back again.
Please suggest.
Thanks for the link James.
Got the solution
User.where(id: "51a7420eb09de918204883c4").find_and_modify({ "$inc" => { points: 1 } })
as per mongoid.org/en/mongoid/docs/querying.html – James Wahlin in comments.
As James said, increment is the way to go:
User.where(id: "51a7420eb09de918204883c4").inc(:points, 100)
This will find all records with the given ID (or whatever you query), increment the value of points by 100, and save it again. Pretty nifty. You can call it on any mongoid query or individual record too, so User.find("some-id").inc(:points, 100) and User.first.inc(:points, 100) will work as well.

Resources