Sistemas Distribuídos Walfredo Cirne Aula 4: Mais Conceitos Básicos As figuras que aparecem nesses slides são de Veríssimo&Rodrigues, reproduzidas com o consentimento dos mesmos. Sincronia • O que significa sincronia? – envio de mensagens blocking non-blocking – iteração same-time different-time – hardware clock-driven – limite superior para execução de ações (processamento e troca de mensagens) Graus de Sincronia • Assíncrono – sem limites de tempo para ações • Síncrono – ações têm limites de tempo conhecidos • Parcialmente síncrono – ações têm limites de tempo para acontecer, mas estes são desconhecidos e/ou válidos somente parte do tempo Qual a gente escolhe? • Assíncrono – fácil de implementar, mas pouco útil • Síncrono – muito poderoso, mas conflita com escala, abertura, interatividade, bom uso dos recursos – usado em sistemas embarcados • Parcialmente síncrono – muito usado em computação de uso geral – semântica tipicamente não é claramente definida Coordenação: Relembrando Exclusão Mútua • Semáforo S: variável não-negativa inteira que só pode se modificada pelos procedimentos up() e down() • down(S) se S > 0: decremente S senão: bloqueia esperando up(S) • up(S) se há alguém bloqueado: desbloqueie senão: incremente S Usando Semáforos (* exclusão mútua *) var mutex: semaphore := 1; thread P1; statement X down(mutex); statement Y up(mutex); statement Z end P1; thread P2; statement A; down(mutex); statement B; up(mutex); statement C; end P2; Exclusão Mútua via Servidor de Lock • Necessário para coordenar processos e evitar “colisões” • Servidor de lock – Interessados em entrar na região crítica mandam mensagem LOCK para o servidor – O servidor só responde com LOCK-GRANTED para um processo – Processo envia UNLOCK ao sair da região • Servidor é ponto único de falhas e possível gargalo de performance • Falha no cliente também é problema Exclusão Mútua via Servidor de Lock Exclusão Mútua Distribuída • Usando um protocolo que garante entrega ordenada total, podemos replicar o servidor de lock em todos os processos do sistema • Podemos também eleger dinamicamente um líder para função de servidor de lock – Precisamos também pensar em como passar estado entre lideres, ou então resetar o sistema quando há troca de líder Exclusão Mútua Distribuída Eleição de Líder • Note a necessidade de sincronia para detecção da falha do líder!! Deadlock • Deadlock ocorre quando um processo fica esperando por outro • Para deadlock é necessário ter exclusão mútua, obtém-e-espera, não-preempção, espera circular • Tudo que você conhece deadlock em sistemas concorrentes vale para sistemas distribuídos, com a complicação que você não tem uma visão global do sistema Consistência • Como garantir que alguma propriedade sistêmica é válida? • Ou seja, como garantir que (ou checar se) a execução do sistema é consistente? • Para checar, podemos parar o sistema e montar um estado global – Naturalmente, isso é dispendioso – Protocolos de snapshot distribuído são uma solução bem mais eficiente para o problema Consenso • Problema básico para coordenação de processos • No consenso, cada processo p propõe um valor vp e decide por um valor final fp • As propriedades de consenso são: – Acordo: p,q que decidem: fp = fq – Validade: p: fp = vp – Terminação: Todo processo correto decide em algum momento no futuro; Consenso em um Sistema sem Falhas Concorrência • “A good understanding of the memory consistency model is paramount to building correct programs” • Consistência atômica – todos os acessos são a mesma memória • Consistência seqüencial – equivalente a consistência atômica de alguma execução – indistinguível da consistência atômica se comunicação é somente via memória Modelo de Memória de Java • Cada thread de Java tem memória local • Dados inexistentes na memória local são copiados da memória global • Eventualmente, dados gravados na memória local são refletidos na memória global • Ao entrar num synchronized, todos os dados da memória local são invalidados • Ao sair de um synchronized, todos os dados gravados são refletidos globalmente Exemplo: Double-Checked Locking class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); } return helper; } } Outro Exemplo: Objetos Ativos public class DataRace extends Thread { int a = 0; public DataRace() { this.start(); a = 1; } public void run() { System.out.println(a); } } Objetos Ativos: O problema é sutil public class DataRace extends Thread { protected int a = 0; public DataRace() { a = 1; this.start(); } } public class ExtendRace extends DataRace { public ExtendRace() { super(); a = 2; } Atomicidade: Suporte a Transações • Transações são uma abstração muito poderosa tipicamente implementadas por banco de dados • Transações são ACID – Atomicity: tudo ou nada – Consistency: atualizações levam os dados de um estado consistente para outro estado consistente – Isolation: transações executadas em paralelo não vêem uma a outra – Durability: é um banco de dados, né? Transações Distribuídas: Two-phase Commit