Created
December 16, 2024 21:08
-
-
Save Cristopheer96/52ab2c0fb9e5a42bb6eb7f6622cdc774 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Características: | |
# Cada nodo tiene un ID único --LISTO | |
# Mantiene una lista de vecinos (otros nodos) --LISTO | |
# Tiene un log interno de eventos y estados --LSITO | |
# - Puede proponer un estado y buscar consenso a través de un algoritmo simplificado | |
# - Puede simular particiones de red y fallos --LISTO | |
# - Puede registrar todos los mensajes y transiciones de estado LSITO | |
class Node | |
attr_reader :id, :log | |
def initialize(id) | |
@id = id | |
@neighbors = [] | |
@log = [] | |
@partitioned_nodes = [] | |
@failed = false | |
@current_value = nil | |
@proposed_value = nil | |
# Almacén simple para guardar votos y propuestas, | |
@received_votes = {} | |
@highest_proposal_value_accepted = 0 # almacenamos el mayor valor previamente aceptado | |
end | |
def add_neighbor(node) | |
@neighbors << node | |
log_event("Nodo #{@id} agregó como vecino al nodo #{node.id} siendo las #{Time.now.strftime('%H:%M:%S')}") | |
end | |
# TRIGGER PARA INICIAR UN NUEVO CONSENSO | |
def propose_state(value) | |
return if @failed | |
@proposed_value = value | |
log_event("Nodo #{@id} propone estado: #{value} siendo las #{Time.now.strftime('%H:%M:%S')}") | |
send_proposal_to_neighbors(value) | |
end | |
def send_proposal_to_neighbors(value) | |
@received_votes = { accepted: 0, rejected: 0 } | |
@neighbors.each do |neighbor| | |
next if partitioned?(neighbor) || neighbor.failed? #Ignoramos a los que no tenemos comunicacion y los que estan Apagados | |
neighbor.receive_proposal(@id, value) | |
end | |
check_consensus | |
end | |
def receive_proposal(from_id, value) | |
return if @failed | |
#LOGICA MADRE: Aceptar esta propuesta si el candidato es mayor o igual a cualquier propuesta aceptada previamente(@highest_proposal_value_accepted). | |
if value >= @highest_proposal_value_accepted | |
# Actualizar el número de propuesta y el valor aceptado | |
log_event("Nodo #{@id} acepta la propuesta Nodo #{from_id} con valor: #{value} siendo las #{Time.now.strftime('%H:%M:%S')}") | |
send_vote_to_node(from_id, :accepted, value) | |
else | |
log_event("Nodo #{@id} rechaza la propuesta Nodo #{from_id} con valor: #{value} siendo las #{Time.now.strftime('%H:%M:%S')}") | |
send_vote_to_node(from_id, :rejected, value) | |
end | |
end | |
def send_vote_to_node(to_id, status, value) | |
log_event("Nodo #{@id} envía voto (#{status}) a Nodo #{to_id} por el valor: #{value}") | |
node = @neighbors.find{ |n| n.id == to_id } #poddemos pasralor apra un metodo | |
return if node.nil? || partitioned?(node) || node.failed? | |
node.receive_vote(@id, status, value) | |
end | |
# Recibir voto de un vecino | |
def receive_vote(from_id, status, value) | |
return if @failed | |
log_event("Nodo #{@id} recibió voto (#{status}) desde Nodo #{from_id} por el valor: #{value}") | |
if status == :accepted | |
@received_votes[:accepted] += 1 | |
else | |
@received_votes[:rejected] += 1 | |
end | |
end | |
def check_consensus | |
return if @proposed_value.nil? | |
total_neighbors = @neighbors.size | |
votes_for = @received_votes[:accepted] | |
#Mayoria simple la mitad +1 | |
if votes_for > total_neighbors / 2.0 | |
@current_value = @proposed_value if @current_value.nil? || @proposed_value > @current_value | |
log_event("Nodo #{@id} alcanzó consenso en el valor: #{@current_value}") | |
@proposed_value = nil | |
else | |
log_event("Nodo #{@id}: no se logró consenso para el valor propuesto #{@proposed_value}") | |
@proposed_value = nil | |
end | |
end | |
def simulate_partition(nodes_array) | |
return if @failed | |
@partitioned_nodes = nodes_array.map(&:id) | |
log_event("Nodo #{@id} simula partición con nodos: #{@partitioned_nodes.join(', ')}") | |
end | |
def retrieve_log | |
"Log del Nodo #{@id}:\n" + @log.join("\n") | |
end | |
def failed? | |
@failed | |
end | |
private | |
def log_event(event) | |
@log << "[#{Time.now.strftime('%H:%M:%S')}] #{event}" | |
end | |
def partitioned?(node) | |
@partitioned_nodes.include?(node.id) | |
end | |
end | |
node1 = Node.new(1) | |
node2 = Node.new(2) | |
node3 = Node.new(3) | |
node4 = Node.new(4) | |
# Conectamos nodoss | |
node1.add_neighbor(node2) | |
node1.add_neighbor(node3) | |
node2.add_neighbor(node1) | |
node2.add_neighbor(node3) | |
node3.add_neighbor(node1) | |
node3.add_neighbor(node2) | |
# Proponer estados | |
node1.propose_state(1) | |
node2.propose_state(2) | |
node3.simulate_partition([node1]) | |
node2.propose_state(3) | |
# Logss | |
puts node1.retrieve_log | |
puts node2.retrieve_log | |
puts node3.retrieve_log |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment