Módulo I: Processamento de Transações

Propaganda
Módulo I: Processamento
de Transações
(Aulas 1 e 2)
Clodis Boscarioli
Agenda:
Introdução:
Apresentação da disciplina;
Leitura do Plano de Ensino;
Conceito de transação;
Estados de uma transação;
Teoria de Serialização:
Serialização de conflito;
Serialização de visão.
Visão Geral
de um SGBD
Processador
de consultas
Usuários
navegantes
Programadores Usuários
Administrade aplicações sofisticados dores de BD
Interface com Programas de
aplicações
aplicações
Consultas
(queries)
Pré-compilador
de comandos
DML
Programas de
aplicações em
código objeto
Usuários
Esquema de
Banco de Dados
Compilador
DML
Interpretador
DDL
Componentes de execução
de consultas
SGBD
Gerenciador
de memória
Gerenciador
de transações
Gerenciador
de buffer
Gerenciador
de arquivos
Armazenamento
em disco
Índices
Arquivos de
dados
Dados
estatísticos
Dicionário
de dados
BD
Justificativa
Um dos critérios de classificação para SGBDs é em relação ao
número de usuários que podem usá-los ao mesmo tempo:
Monousuário: apenas um usuário pode utilizar o sistema em um
momento;
Multiusuário: vários usuários podem utilizar o sistema em um momento.
Sistemas de BD que são projetados para uso em um micro-computador;
Sistemas de reserva de passagens aéreas;
Sistemas bancários/financeiros;
Sistemas de venda para WEB.
Neste contexto, insere-se o conceito de multiprogramação. É
permitida a existência de SGBDs multiusuários porque possibilita-se
a execução de vários programas ao “mesmo tempo”.
Na realidade, se existe apenas uma CPU, então parte dos programas
são intercalados na execução e, a cada “unidade de tempo”, um
programa apenas é executado;
Comandos de um programa são executados e então o programa é
suspenso para que comandos de um outro programa sejam
executados.
A
A
B
t1
C
Uma única
CPU
CPU2
D
B
CPU1
tn
t1
tn
tempo
Em SGBDs os dados do BD são os recursos primários que podem ser lidos/
recuperados e modificados.
A execução de um programa de acesso e/ou modificação ao conteúdo do BD é
chamado de transação.
Transações
Um conjunto de várias operação em um BD pode ser visto pelo usuário
como uma única unidade.
Exemplo:
A transferência de fundos de uma conta corrente pra uma conta poupança é
uma operação única do ponto de vista do cliente, porém, dentro do sistemas de
banco de dados, ela envolve várias operações.
É essencial que todo o conjunto de operações seja concluído, ou que, no
caso de uma falha, nenhuma delas ocorra.
Essas operações, que formam uma única unidade lógica de trabalho são
chamadas de transações.
Um SGBD precisa:
Garantir que a execução da transação seja completa;
Administrar a execução simultânea de várias transações evitando
inconsistências;
Uma transação que calcula o total de dinheiro do cliente poderia trabalhar com o saldo
da conta corrente antes do débito feito pela transação de transferência e, também,
verificar o saldo da poupança depois do crédito. Com isso, obteria um resultado
incorreto.
Operações de Leitura/Escrita
As operações de acesso ao BD que uma transação
pode fazer são:
read(x): que transfere o item de dados x do BD para um buffer
local alocado à transação que executou a operação de read. O
valor de x é colocado dentro de uma variável de programa.
write(x): que transfere o item de dados x do buffer local da
transação que executou o write de volta para o BD. O valor da
variável de programa é passado para o item de dado no BD.
OBS.: para fins de simplificação, o item de dados e a variável de
programa correspondente serão representados da mesma
forma, x.
Execução das Operações
Executar read(x):
Encontrar o endereço do bloco de disco que contém x;
Copiar este bloco de disco para dentro do buffer/memória principal, se
ele já não estiver lá;
Copiar o item x do buffer para a variável de programa.
Executar write(x):
Encontrar o endereço do bloco de disco que contém x;
Copiar o bloco de disco para a memória principal se ele já não estiver
lá;
Copiar o item x da variável de programa x para a localização correta no
buffer;
Copiar o bloco alterado do buffer de volta para o disco (imediatamente
ou mais tarde).
Exemplo de Transação
A)
read(x);
x := x – N;
write(x);
read(y);
y := y + N;
write(y);
T1
Acesso concorrente ao dado x.
B)
read(x);
x := x + M;
write(x);
T2
Outro Exemplo
Seja T1 uma transação que transfere 50 reais da conta A
para a conta B. Essa transação pode ser definida como:
T1:
read(A);
A := A - 50;
write(A);
read(B);
B := B + 50;
write(B);
Propriedades das Transações (ACID)
A – Atomicidade: ou todas as operações da transação são refletidas
corretamente no banco de dados ou nenhuma o será.
Suponha que, durante a execução da transação Ti, uma falha aconteça
impedindo Ti de se completar com sucesso (uma queda de energia).
Suponha ainda que a falha tenha ocorrido depois da execução da
operação write(A), mas antes da operação write(B). O que acontece?
O estado do sistema não reflete mais um estado real do mundo que se
supõe representado no BD. É um estado inconsistente.
Estes estados não podem ser perceptíveis. Ou seja, estados
inconsistente só são permitidos no momento de execução da
transação, onde o usuário não terá qualquer tipo de acesso aos dados.
Assegurar a atomicidade de uma transação é responsabilidade do
SGBD, mais especificamente, do Componente de Gerenciamento
de Transações e Recuperador de Falhas.
Propriedades das transações (ACID)
C - Consistência: a execução de uma transação
isolada (ou seja, sem a execução concorrente
de outra transação) preserva a consistência do
banco de dados.
A
exigência da consistência aqui significa que a soma
de A com B deve permanecer inalterada após a
execução da transação.
Se
o banco de dados está consistente antes de uma
execução de transação, o banco de dados
permanece consistente após a execução da
transação.
Responsabilidade
do programador.
Propriedades das transações (ACID)
I – Isolamento: embora diversas transações possam ser executadas
de forma concorrente, o sistema garante que, para todo par de
transações Ti e Tj, Ti tem a sensação de que Tj terminou sua
execução antes de Ti começar, ou vice-versa. Cada transação não
toma conhecimento de outras transações concorrentes a ela no
sistema.
A execução concorrente de várias transações implica no intercalamento
de suas operações. Se este intercalamento é feito de forma
inconveniente, inconsistências serão geradas.
No exemplo dado, se uma transação concorrente ler o valor de A antes
dele ser escrito mais depois do processamento A-50 ter sido feito, ela
estará lendo um valor inconsistente.
Execuções em série resolvem este problema mas a concorrência é
benéfica por causa do tempo de execução (melhora do desempenho).
Assegurar o isolamento é de responsabilidade do Controlador de
Concorrência.
Propriedades das transações (ACID)
D – Durabilidade: depois da transação
completar-se com sucesso, as mudanças que
ela faz no banco de dados persistem, até
mesmo se houver falhas no sistema.
No
exemplo dado, se a transação se completar com
sucesso e o usuário que a disparou for notificado da
transferência de fundos, isso significa que não houve
nenhuma falha de sistema que tenha resultado em
perda de dados relativa a essa transferência.
Assegurar
a durabilidade é responsabilidade do
componente do SGBD chamado de Recuperador
de Falhas.
Operações Adicionais
Uma transação é uma unidade de trabalho atômica que é
completada em sua totalidade ou nada dela deve ter efeito. Para
tornar isso possível, as seguintes operações são necessárias:
Begin-transaction: Denota o início da execução da transação;
End-transaction: Especifica que as operações da transação terminaram
e marca o limite final da execução da transação. Neste ponto é
necessário verificar se o COMMIT ou o ABORT são necessários, caso
já não tenham sido explicitados;
Commit-transaction: Sinal de término com sucesso e que as alterações
podem ser “permanentemente” gravadas no BD;
Rollback (abort-transaction): Assinala que a transação não terminou
com sucesso e que seus efeitos devem ser desfeitos;
Undo: Desfaz uma operação;
Redo: Refaz uma operação.
Exemplo com Operadores (1)
Procedure Transfer;
begin
start; (begin transaction)
input (FROMaccount, TOaccount, amount);
temp := read(Accounts[FROMaccount]);
if temp < amount then
begin
output(“insufficient funds”);
Abort;
end
else
begin
write(Accounts[FROMaccount],temp - amount);
temp = read(Accounts[TOaccount]);
write(Accounts[TOaccount], temp + amount);
commit;
output(“transfer complete”);
end;
return; (end transaction)
end.
Exemplo com Operadores (2)
begin transaction
update TABELA1 set CAMPO1 = ‘NOVO VALOR’
where CAMPO2 = 35
if @@ERROR <> 0
rollback
else
commit
end transaction
Suponha uma tabela que possua 6000 tuplas que satisfaçam a condição e que
na tupla 4666 houve uma falha no sistema. Neste caso, a variável chamada
@@ERROR guardará o código do erro, o comando ROLLBACK será executado
e as 4666 tuplas já atualizadas serão automaticamente voltadas a seu estado
anterior.
Ao contrário, se tudo correr bem, o comando COMMIT será executado e todas
as 6000 tuplas serão atualizadas com êxito.
Estados de uma Transação
Read/Write
Begin
Transaction
Ativa
End
Transaction Em efetivação
parcial
Abort
Abort
Em
falha
Diagrama de transição de estados
para a execução da transação.
Efetivada
Completa
Com sucesso
ou com falha.
Considerações
Uma transação entra no estado de falha quando o
sistema determina que ela já não pode prosseguir sua
execução normal. Essa transação deve ser desfeita e
entra no estado de abortada. Nesse momento o sistema
tem duas opções:
Reiniciar a transação: somente se ela foi abortada como
resultado de algum erro de hardware ou de software não criado
pela lógica interna da transação. Uma transação reiniciada é
considerada uma transação nova.
Matar a transação: normalmente, isto é feito em decorrência de
algum erro lógico interno e só pode ser corrigido refazendo o
programa de aplicação. Esse erro dá-se ou porque a entrada de
dados não era adequada ou porque os dados desejados não
foram encontrados no banco de dados.
Considerações
Escritas externas observáveis (escrever em um
terminal ou em uma impressora):
Uma escrita desse tipo não pode ser apagada já que
é vista externamente ao sistema de banco de dados.
Geralmente somente são permitidas após a transação
ser efetivada.
Execução Concorrente
Permitir que múltiplas transações concorram na atualização de dados traz
diversas complicações em relação à consistência desses dados.
Razões para permitir a concorrência:
Uma transação consiste em diversos passos. Alguns envolvem atividades
de I/O, outros atividades de CPU. A CPU e os discos podem operar em
paralelo. Logo, a atividade de I/O pode ser feita em paralelo com o
processamento na CPU, por meio das execuções paralelas das transações.
Enquanto uma leitura ou escrita solicitada por uma transação está em
desenvolvimento em um disco, outra transação pode ser processada na
CPU, e outro disco pode estar executando uma leitura ou escrita solicitada
por uma terceira transação. Assim aumenta-se o rendimento do sistema, ou
seja, o número de transações que podem ser executadas em um
determinado tempo.
Pode haver uma mistura de transações em execução simultânea no
sistema, algumas curtas e outras longas. Se a execução das transações for
seqüencial, uma transação curta pode ser obrigada a esperar até que uma
transação longa precedente se complete. Com a concorrência reduz-se o
tempo médio de resposta, ou seja, o tempo médio para uma transação ser
concluída após sua submissão.
Execução Concorrente
Sejam T1 e T2 duas transações que transferem
fundos de uma conta para outra.
A transação T1 transfere 50 dólares da conta A
para a conta B.
A transação T2 transfere 10 por cento do saldo
da conta A para a conta B.
Execução Concorrente
T1:
read(A);
A := A – 50;
write(A);
read(B);
B := B + 50;
write(B);
T2:
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + temp;
write(B);
Execução Seqüencial
Escalas de execução em seqüência: Observe que o estado do BD é sempre
consistente.
T1
T2
T1
T2
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + temp;
write(B);
read(A);
A := A – 50;
write(A);
read(B);
B := B + 50;
write(B);
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + temp;
write(B);
read(A);
A := A – 50;
write(A);
read(B);
B := B + 50;
write(B);
Execução Concorrente
Incorreta
Correta
T1
T2
read(A);
A := A – 50;
write(A);
T1
T2
read(A);
A := A – 50;
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + 50;
write(B);
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
write(A);
read(B);
B := B + 50;
write(B);
read(B);
B := B + temp;
write(B);
read(B);
B := B + temp;
write(B);
Problemas na Concorrência
Problema de alterações perdidas;
Problema de alteração temporária;
Problema do resumo incorreto.
Problema de Alterações Perdidas
Ocorre quando duas transações que acessam o
mesmo item de BD possuem suas operações
intercaladas de uma maneira que o valor de
algum item de dado fique incorreto.
T1
T2
read(x);
x := x - N;
read(x)
x := x + M;
write(x);
read(y);
write(x)
y := y + N;
write(y)
Problema de Alteração Temporária
Ocorre quando uma transação altera um item de
dados e depois ela falha por alguma razão. O
item de dado é acessado por outra transação
antes que o valor original seja estabelecido.
T1
T2
read(x);
x := x - N;
write(x);
read(x)
x := x + M;
write(x)
read(y);
falha
Problema de Resumo Incorreto
Se uma transação está calculando uma função agregada com um
conjunto de tuplas e outras transações estão alterando algumas
destas tuplas, a função agregada pode calcular alguns valores
antes deles serem alterados e outros depois de serem alterados.
T1
T3
sum := 0;
read(a);
sum : sum + a;
read(x);
x := x - N;
write(x);
read(y);
y := y + N;
write(y)
read(x)
sum := sum + x;
read(y);
sum := sum + y;
Serialização (Seriação)
O sistema de banco de dados deve controlar a execução
concorrente de transações para assegurar que o estado do BD
permaneça consistente.
A consistência do banco de dados, sob execução concorrente, pode
ser assegurada por meio da garantia de que qualquer escala
executada tenha o mesmo efeito de outra que tivesse sido
executada sem qualquer concorrência.
Isto é, uma escala de execução deve, de alguma forma, ser
equivalente a uma escala seqüencial.
Formas de equivalência entre escalas de execução podem ser
verificadas sob dois prismas:
Por conflito;
Por visão.
Serialização de Conflito
Considere uma escala de execução S com duas
intruções sucessivas, Ii e Ij, das transações Ti e
Tj (i <> j), respectivamente.
Se Ii e Ij referem-se a itens de dados diferentes,
então é permitido alternar Ii e Ij sem afetar os
resultados de qualquer instrução da escala.
Se Ii e Ij referem-se ao mesmo item de dado Q,
então a ordem dos dois passos pode importar.
Serialização de Conflito
1. Ii = read(Q) e Ij = read(Q)
A seqüência de execução de Ii e Ij não importa, já que o mesmo valor de Q
é lido por Ti e Tj, independentemente da ordem destas operações.
2. Ii = read(Q) e Ij = write(Q)
Se Ii vier antes de Ij, então Ti não lê o valor de Q que é escrito por Tj na
instrução Ij.
Se Ij vier antes de Ii, então Ti lê o valor de Q que é escrito por Tj.
Assim, a ordem de Ii e Ij importa.
Serialização de Conflito
3. Ii = write(Q) e Ij = read(Q)
A ordem de Ii e Ij importa por razões semelhantes às do caso
anterior.
4. Ii = write(Q) e Ij = write(Q)
Como ambas as instruções são operações de escrita, a ordem
dessas instruções não afeta Ti ou Tj.
Entretanto, o valor obtido pela próxima instrução read(Q) em S é
afetado, já que somente o resultado da última das duas
instruções write(Q) é preservado no banco de dados.
Se não houver nenhuma outra instrução de write(Q) depois de Ii
e Ij em S, então a ordem de Ii e Ij afeta diretamente o valor final de
Q no que se refere ao estado do banco de dados após a
execução da escala S.
Serialização de Conflito
Diz-se que duas instruções entram em conflito se elas
são operações pertencentes a transações diferentes,
agindo no mesmo item de dado, e pelo menos uma
dessas instruções é uma operação de escrita.
T1
T2
A instrução write(A) de T1 entra em conflito
com a instrução read(A) de T2.
read(A)
write(A)
read(A)
write(A)
read(B)
write(B)
read(B)
write(B)
Porém, a instrução write(A) e T2 não está em
conflito com a instrução read(B) de T1.
Serialização de Conflito
Seja Ii e Ij instruções consecutivas de uma
escala de execução S.
Se Ii e Ij são instruções de transações diferentes
e não entram em conflito, então podemos trocar
a ordem de Ii e Ij para produzir uma nova escala
de execução S’.
Diz-se que S e S’ são equivalentes já que todas
as instruções aparecem na mesma ordem em
ambas as escalas de execução com exceção de
Ii e Ij, cuja ordem não importa.
Serialização de Conflito
Ainda segundo a escala S, a instrução write(A) de T2
não entra em conflito com a instrução read(B) de T1.
Então, é permitido mudar a ordem dessas instruções
para gerar uma escala de execução equivalente.
T1
T2
read(A)
write(A)
read(A)
read(B)
write(A)
write(B)
read(B)
write(B)
Em relação a um mesmo estado inicial do
sistema, ambas as escalas (S e esta)
produzem o mesmo estado final no sistema.
Serialização de Conflito
Se as seguintes trocas de instruções não-conflitantes
forem feitas:
read(B) de T1 por read(A) de T2;
write(B) de T1 por write(A) de T2;
write(B) de T1 por read(A) de T2.
... uma escala com execuções seriais será obtida.
Assim mostrou-se que a escala S é equivalente, por
conflito, a uma escala seqüencial.
Essa equivalência garante que para um mesmo estado
inicial do sistema, a escala S produzirá o mesmo estado
final produzido por essa escala seqüencial.
Serialização de Conflito
Se uma escala de execução S puder ser transformada
em outra, S’, por uma série de trocas de instruções nãoconflitantes, dizemos que S e S’ são equivalentes por
conflito.
O conceito de equivalência por conflito leva ao conceito
de serialização por conflito.
Uma escala de execução S é serializável por conflito se
ela é equivalente por conflito a uma escala de execução
seqüencial.
Serialização de Conflito
A escala abaixo não é serializável por conflito
pois não é equivalente em conflito nem à escala
seqüencial <T3,T4> nem <T4,T3>.
T3
T4
read(A)
write(A)
write(A)
Serialização de Conflito
A escala abaixo não é serializável por conflito, mas produz o
mesmo resultado que uma escala seqüencial. Confira.
T1
T2
read(A);
A := A – 50;
write(A);
read(B);
B := B – 10;
write(B);
read(B);
B := B + 50;
write(B);
read(A);
A := A + 10;
write(A);
Teste de Serialização de Conflito
Seja S uma escala. Para saber se ela é serializável em
relação às operações conflitantes é necessário criar um
grafo de precedência para S.
G = (V,E) em que V é um conjunto de vértices e E é um
conjunto de arestas.
O conjunto de vértices é composto por todas as transações que
participam da escala.
O conjunto de arestas consiste em todas as arestas Ti Tj para
as quais uma das seguintes condições é verdadeira:
Ti executa write(Q) antes de Tj executar read(Q);
Ti executa read(Q) antes de Tj executar write(Q);
Ti executa write(Q) antes de Tj executar write(Q);
Teste de Serialização de Conflito
Se existir uma aresta Ti Tj no grafo de precedência,
então, em qualquer escala seqüencial S’ equivalente a
S, Ti deve aparecer antes de Tj.
Exemplo:
T1
T2
read(A);
A := A – 50;
write(A);
read(B);
B := B + 50;
write(B);
T1
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + temp;
write(B);
T2
Teste de Serialização de Conflito
Exemplo:
T1
T2
T1
T2
read(A)
A := A - 50
read(A)
temp := A * 0,1
A := A – temp
write(A)
read(B)
write(A)
read(B)
B := B + 50
write(B)
B := B + temp;
write(B)
Ciclo no grafo: Se o grafo de precedência
possui ciclo, então a escala S não é
serializável por conflito.
A ordem de serialização pode ser obtida por
meio da classificação topológica, que
estabelece uma ordem linear para a escala
consistente com a ordem parcial do grafo
de precedência.
Serialização de Visão
Trata-se de uma forma de equivalência menos restritiva que a
equivalência de conflito.
Considere duas escalas de execução S e S’, com o mesmo
conjunto de transações participando de ambas. As escalas S e S’
são ditas equivalentes por visão se as três condições seguintes
forem satisfeitas:
Para cada item de dado Q, se a transação Ti fizer uma leitura no valor
inicial de Q na escala S, então a transação Ti também deve, na escala
S’, ler o valor inicial de Q;
Para cada item de dado Q, se a transação Ti executar um read(Q) na
escala S, e aquele valor foi produzido por meio da transação Tj (se
houver), então a transação Ti também deverá, na escala S’, ler o valor
de Q que foi produzido por meio da transação Tj;
Para cada item de dado Q, a transação (se houver) que executa a
operação final de write(Q) na escala S tem de executar a operação de
write(Q) final na escala S’;
Serialização de Visão
T1
T2
T1
T2
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + temp;
write(B);
read(A);
A := A – 50;
write(A);
read(B);
B := B + 50;
write(B);
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + temp;
write(B);
read(A);
A := A – 50;
write(A);
read(B);
B := B + 50;
write(B);
Não são equivalentes por visão
T1
T2
read(A);
A := A – 50;
write(A);
read(A);
temp := A * 0,1;
A := A – temp;
write(A);
read(B);
B := B + 50;
write(B);
read(B);
B := B + temp;
write(B);
São equivalentes por visão
Serialização de Visão
O conceito de equivalência por visão leva ao
conceito de serialização por visão.
Uma escala é serializável por visão se ela for
equivalente em visão a uma escala de execução
seqüencial.
T1
T2
T3
É equivalente em visão à escala
seqüencial <T1,T2,T3> já que
uma instrução read(Q) lê o valor
inicial de Q em ambas as escalas
e T3 executa a escrita final de Q
em ambas as escalas.
read(Q)
write(Q)
write(Q)
write(Q)
Serialização de Visão
Modifica-se o grafo de precedência para
serialização por conflito para testar se o
escalonamento é serializável por visão.
Verificando se o escalonamento abaixo é
serializável por conflito:
T1
T2
T3
T1
T2
read(Q)
write(Q)
T3
write(Q)
write(Q)
Gravações inúteis
Serialização de Visão
Vejamos a seguinte situação:
Seja S uma escala.
Suponha que a transação Tj leia o valor do item de dado
Q escrito por Ti. Se S é serializável por visão, então
qualquer em escala seqüencial S’, à qual S é
equivalente, Ti deve preceder Tj.
Suponha agora que, na escala S, a transação Tk
executou um write(Q). Então, na escala S’, Tk deve
preceder Ti ou deve seguir Tj. Ela não poderá aparecer
entre Ti e Tj, porque dessa forma Tj não leria o valor de
Q escrito por Ti e, S e S’ não seriam equivalentes por
visão.
Serialização por Visão
Se o grafo não contiver ciclos então o escalonamento é serializável
por visão.
Se o grafo contiver ciclos não significa, necessariamente, que o
escalonamento não é serializável.
Haverá então 2n grafos diferentes, sendo que cada um contém
apenas uma aresta de cada par.
Se alguns desses grafos for acíclico, sabe-se que a escala
correspondente é serializável por visão.
O escalonamento serial equivalente é obtido seguindo a ordenação
topológica do grafo acíclico.
Exemplo de Ordenação Topológica
Ti
Ti
Tj
Tk
Ti
Tj
Tk
ou
Tk
Tj
Tm
Tm
Tm
Fontes Bibliográficas:
Sistemas de Banco de Dados (Cap. 15). Abraham
Silberchatz, Henry F. Korth, S Sudarshan. 5ª Ed.
Elsevier, 2006.
Sistemas de Banco de Dados (Cap. 17). Ramez
Elmasri e Sham Navathe. 4ª Ed. Pearson, 2005.
Controle de Concorrência e Distribuição de Dados:
A teoria clássica, suas limitações e extensões
modernas. João Eduardo Ferreira e Marcelo Finger.
São Paulo: Escola Brasileira de Computação, 2001.
Download