Author Archives: Luis Fernando Chaim

Kafka – Introdução

Introdução

O Apache Kafka é um sistema de código aberto com envio distribuído de mensagens que permite que você crie aplicativos em tempo real usando dados de streaming.

Você pode enviar dados de streaming, como sequências de cliques de sites, transações financeiras e logs de aplicativo, para o cluster do Kafka. Ele reserva os dados e os distribui para aplicativos de processamento de streams incorporados às estruturas, como Apache Spark Streaming, Apache Storm ou Apache Samza.

Desde leitura e escrita direta entre diversos bancos e mecanismos de pesquisas (SQL, MongoDB, RethinkDB, ElasticSearch) até materialização de dados on streaming com KTable por exemplo, é possível utilizar o Kafka de diversas maneiras para mover e transformar grande volume de dados.

Conceitos

Mensagens

Mensagem é o principal recurso do Kafka. Todos os eventos do Kafka podem ser resumidos em mensagens, sendo consumidas e produzidas através de tópicos. Uma mensagem pode ser desde uma simples String com “Hello World!” ou até mesmo um JSON contendo um objeto do seu domínio.

O Kafka permite definir Schemas para mensagens, como por exemplo utilizando o Avro. Como num exemplo de um JSON contendo um objeto do seu domínio, o Schema pode auxiliar impedindo que mensagens contendo conteúdos inválidos sejam trafegadas no tópico.

Uma mensagem pode também ser composta por uma chave (key/value), que é utilizada para sharding e compactação dentro do Kafka. Assim em um ambiente distribuído, é garantido a ordem das mensagens uma vez que mensagens com a mesma chaves são direcionadas para uma única partição do Kafka.

Tópicos

Um tópico é como categorizamos grupos de mensagens dentro do Kafka. Todas as mensagens enviadas para o Kafka permanecem em um tópico. Como comentado sobre Event Sourcing, mensagens são imutáveis e ordenadas.

Para manter a ordenação em um ecossistema de Kafka, os tópicos possuem partições e fatores de replicação. Um tópico pode possuir n partições, mas ao receber uma nova mensagem o Kafka automaticamente direciona aquela mensagem para uma partição específica dependendo de sua chave (key). Assim mensagens de uma mesma chave estarão apenas em uma única partição, garantindo assim a leitura ordenada de todas as mensagens de um tópico.

Producer

Um Kafka Producer é responsável por enviar uma mensagem para um tópico específico. De forma simples, você pode produzir uma mensagem em um tópico.

Uma vez que uma mensagem é produzida em um tópico o próprio Kafka organiza a mensagem em uma partição, garantindo sempre a ordem das mensagens produzidas, como citado anteriormente.

Consumer

Temos os tópicos, e as mensagens dentro dos tópicos. Com o Kafka Consumer é possível ler essas mensagens de volta. Importante entender que, ao ler uma mensagem com o consumer, a mensagem não é retirada do tópico.

Você pode ter vários Kafka Consumers conectados em um mesmo tópico, e cada um terá a posição onde parou de ler. Assim você pode ter um tópico produzindo como no exemplo acima, pontuações de um jogador, e ter diversos consumers em pontos diferentes do tópico realizando ações diferentes. Você também pode escolher ter vários consumers lendo o mesmo tópico e na mesma partição, para escalar sua aplicação por exemplo, neste caso estes consumers fariam parte de um Consumer Group, e compartilharão sempre a posição final de leitura entre eles (offsets).

Apache Zookeeper

O Zookeeper é um serviço centralizado para, entre outras coisas, coordenação de sistemas distribuídos. O Kafka é um sistema distribuído, e consequentemente delega diversas funções de gerenciamento e coordenação para o Zookeeper.

Eles possuem uma dependência muito forte, mas isso não é tão ruim. O Kafka pode fazer o que ele intencionalmente tem que saber fazer de melhor, delegando essas demais funcionalidades para quem sabe fazer isso bem, sem precisar reinventar a roda.

Kafka Brokers / Clusters

O Broker é o coração do ecossistema do Kafka. Um Kafka Broker é executado em uma única instância em sua máquina. Um conjunto de Brokers entre diversas máquinas formam um Kafka Cluster.

Uma das principais características do Kafka é a escalabilidade e resiliência que ele oferece. Você pode rodar o Kafka local na sua máquina onde sua própria máquina teria um Kafka Broker formando um Kafka Cluster, como pode subir n instâncias de Kafka Brokers e todas estarem no mesmo Kafka Cluster. Com isso é possível escalar sua aplicação, e replicar os dados entre os Brokers.

Code Playgrounds – Introdução e soluções

Introdução

Code Playground é uma ferramenta para editar e testar aplicações Web, utilizando HTML, CSS e JavaScript de forma online.

Com esse tipo de ferramenta é possível configurar pré-compiladores HTML, CSS e JavaScript, por exemplo:

  • HTML – Hml, Markdown, Slim e Pug
  • CSS – LESS, SCSS, Sass, Stylus e PostCSS
  • Javascript – CoffeeScript, LiveScript, TypeScript e Babel

Soluções

Abaixo, seguem algumas boas soluções:

  • Codepen – http://codepen.io/
  • JSBin – http://jsbin.com/
  • Plunker – https://plnkr.co/
  • CSSDeck – http://cssdeck.com/
  • Dabblet – http://dabblet.com/
  • Liveweave – http://liveweave.com/
  • JSFiddle – http://jsfiddle.net/

Conclusão

Entre os que mais se destacam, encontram-se o Codepen (possui mais pré-processadores Javascript), CSSDeck (bastante utilizado). Porém, assim como ocorrem com editores (Visual Studio Code, Atom, Sublime e Vim), a escolha depende da melhor adequação da ferramenta ao desenvolvedor.

Desenvolvendo uma aplicação Spring Data REST e H2

Desenvolvendo uma aplicação Spring Data, REST e H2

Criando a aplicação

Acesse o site http://start.spring.io/ e configure uma aplicação conforme imagem abaixo.

Baixe e descompacte o arquivo (spring-rest-data-h2.zip) em algum diretório de trabalho.

Abra o Eclipse e importe o projeto (Maven Project).

Criação da classe controller

No Eclipse, clique em File / New / Java Class e defina-a conforme abaixo.

Código

package br.com.whs.springrestdatah2.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import br.com.whs.springrestdatah2.Model.User;
import br.com.whs.springrestdatah2.repository.UserRepository;

@RestController
@RequestMapping("/user")
public class UserController {
        @Autowired
        private UserRepository userRepo;
    
        @RequestMapping(value = "/list", method = RequestMethod.GET)
        public List<User> findAll(){
                return userRepo.findAll();
        }

        @RequestMapping(value = "/find-by-id/{id}", method = RequestMethod.GET)
        public ResponseEntity<?> findById(@PathVariable final Long id){
                User user = userRepo.findOne(id);
                if( user == null ){
                        String msg = "{\"message\":\"User with id " + id + " not found.\"}";
                        return new ResponseEntity<String>(msg,HttpStatus.NOT_FOUND);
                }else{
                        return new ResponseEntity<User>(user,HttpStatus.OK);
                }
        }

        @RequestMapping(value = "/find-by-login/{loginName}", method = RequestMethod.GET)
        public ResponseEntity<?> findByLoginName(@PathVariable final String loginName){
                User user = userRepo.findByLoginName(loginName);
                if( user == null ){
                        String msg = "{\"message\":\"User with login " + loginName + " not found.\"}";
                        return new ResponseEntity<String>(msg,HttpStatus.NOT_FOUND);
                }else{
                        return new ResponseEntity<User>(user,HttpStatus.OK);
                }
        }
        @RequestMapping(value = "/create", method = RequestMethod.POST)
        public ResponseEntity<User> create(@RequestBody final User user){
                userRepo.save(user);
                return new ResponseEntity<User>(userRepo.findByLoginName(user.getLoginName()),HttpStatus.OK);
        }

        @RequestMapping(value = "/update/{id}", method = RequestMethod.PUT)
        public ResponseEntity<?> update(@PathVariable("id") long id, @RequestBody User user) {
            User userData = userRepo.findOne(id);
            if (userData == null) {
                String msg = "{\"message\":\"User with id " + id + " not found.\"}";
                return new ResponseEntity<String>(msg,HttpStatus.NOT_FOUND);
            }
            userData.setLoginName(user.getLoginName());
            userData.setFullName(user.getFullName());
            userData.setPassword(user.getPassword());
            userRepo.save(userData);
            return new ResponseEntity<User>(userData, HttpStatus.OK);
        }
    
        @RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
        public ResponseEntity<?> delete(@PathVariable("id") long id) {
            User user = userRepo.findOne(id);
            if (user == null) {
                String msg = "{\"message\":\"User with id " + id + " not found.\"}";
                return new ResponseEntity<String>(msg,HttpStatus.NOT_FOUND);
            }
            userRepo.delete(id);
            return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
        }
}

Criando a interface UserRepository

Código

package br.com.whs.springrestdatah2.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;

import br.com.whs.springrestdatah2.Model.User;

@Component
public interface UserRepository extends JpaRepository<User, Long>{
    public User findByLoginName(String loginName);
}

 

Criando a classe Model (User.java)

Código

package br.com.whs.springrestdatah2.Model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;
    private String loginName;
    private String fullName;
    private String password;
public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getLoginName() {
    return loginName;
}

public void setLoginName(String loginName) {
    this.loginName = loginName;
}

public String getFullName() {
    return fullName;
}

public void setFullName(String fullName) {
    this.fullName = fullName;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

}

 

Rodando a aplicação

Essa é uma aplicação Spring Boot, onde o framework baixará e utilizará os recursos necessários, conforme a configuração (definida na Criação da Aplicação). Neste caso, será utilizado Tomcat e banco de dados H2 (ambos embedded)

Procedimento

  1. Abra a classe SpringRestDataH2Application.java
  2. Clique no menu Run / Run as / Java Aplication
  3. Espere até que apareça “Tomcat started on port(s): 8080 (http)”
  4. Abra um navegador e forneça a url de listagem (http://localhost:8080/user/list)
  5. Deverá aparecer apenas 2 colchetes ([]), pois o List está vazio

 

Postman

Para facilitar as chamadas de POST (/user/create), PUT (/user/update) e DELETE (/user/delete), vou utilizar o Postman.

Método POST

Método PUT

Método DELETE

Código-Fonte (github)

https://github.com/lfchaim/spring-rest-data-h2

 

Criando projeto JAX-RS

Introdução

O objetivo deste post é demonstrar como criar um projeto RESTful, usando Eclipse e Jersey.

Procedimento

Instalando archetype-catalog do Jersey

  1. Abra o Eclipse
  2. Vá em Window / Preferences
  3. Abra o item Maven
  4. Selecione Archetype
  5. Clique em Add Remote Catalog…
  6. Em Catalog File: http://repo1.maven.org/maven2/archetype-catalog.xml
  7. Em Description: Maven Catalog
  8. Clique em OK

Criando o projeto

  1. Vá em File / New / Other…
  2. Digite: Maven
  3. Selecione: Maven Project
  4. Clique em Next
  5. Digite em Filter: jersey-quickstart
  6. Selecione: jersey-quickstart-webapp (versão 2.26)
  7. Clique em Next
  8. Digite Group Id: br.com.whs.jaxrsdemo
  9. Artifact Id: jax-rs-demo
  10. Version: 1.0.0
  11. Package: br.com.whs.jaxrsdemo
  12. Clique em Finish

Instalando Tomcat

  1. Acesse a página: https://tomcat.apache.org/download-80.cgi
  2. Baixe a versão 8.5.24
  3. Descompacte no local desejado
  4. No Eclipse, clique na aba: Servers
  5. Clique no link: Click this link to create a new server…
  6. Selecione: Apache
  7. Selecione: Tomcat v8.5 Server
  8. Clique em Next
  9. Localize o caminho raiz da instalação
  10. Clique em Next
  11. Adicione o projeto recém criado
  12. Clique em Finish

Testando Aplicação

  1. Clique no servidor criado (aba Servers)
  2. Clique no botão Start (mesma janela de Servers)
  3. Abra um navegador e digite: http://localhost:8080/jax-rs-demo/
  4. Clique no link Jersey resource

Código-fonte do projeto

Você pode baixar os fontes desse projeto no github.

Instalando NodeJs usando NVM no Ubuntu

Introdução

Neste post, vamos instalar o NodeJs, através da ferramenta NVM (Node Version Manager).

NVM

Excelente ferramenta de administração de versões do NodeJs. Procure utilizar essa ferramenta, pois torna o trabalho bem mais fácil.

Instalação do NVM

  1. Abra um Terminal
  2. Curl
    1. curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
  3. Wget
    1. wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.23.2/install.sh | bash
  4. Digite
    1. nvm
    2. Deve aparecer o help do comando (já está instalado)

Instalação do NodeJs

Agora sim, vai ser complicado.

  1. No Terminal, digite
    1. nvm install 8.9.1
    2. 8.9.1 foi a versão estável selecionada no momento desse post
  2. Basta digitar
    1. node -v (aparecerá v8.9.1)
    2. npm -v (aparecerá 5.5.1)

Mamão com açucar! Está pronto!

 

MongoDB – Primeiros Comandos – Linux

Introdução

Este post tem o objetivo de iniciar a utilização do MongoDB, usando comandos.

Pré-requisitos

Para começarmos, é necessário ter executado o procedimento do post Instalar MongoDB no Ubuntu.

Console do MongoDB

Para abrir o console do MongoDB, utilize o comando

$ mongo

Listando bancos

> show dbs

admin 0.000GB
escola 0.000GB
local 0.000GB

Criando ou entrando no banco

Para entrar ou criar um banco, utilize o comando “use”. Se o banco não existir, será criado automaticamente.

> use escola

switched to db escola

Criando tabela e inserindo dado

Podemos criar uma tabela e inserir o dado ao mesmo tempo. Para isso, utilizamos o comando db.[[TABELA]].insert.

> db.aluno.insert({‘nome’:’Luis Fernando Chaim’,’dataNasc’:new Date(‘1972-05-22’)})

WriteResult({ “nInserted” : 1 })

Listando tabelas

> show collections

aluno

Pesquisando dado

Para pesquisar os dados de uma tabela, utilizar db.[[TABELA]].find().

> db.aluno.find()

{ “_id” : ObjectId(“5a02e14ef52458010bddaf80”), “nome” : “Luis Fernando Chaim”, “dataNasc” : ISODate(“1972-05-22T00:00:00Z”) }

> db.aluno.find({nome: ‘Luis Fernando Chaim’})

Exemplos

db.professores.insert({nome: ‘Jean’, idade:26, disciplinas:[‘Tecnicas de Programacao’, ‘Topicos Avancados’], sexo: ‘m’})

db.professores.insert({nome: ‘John’, idade:46, disciplinas:[‘Tecnicas de Programacao’, ‘Topicos Avancados’], sexo: ‘m’})

db.professores.insert({nome: ‘Joseph’, idade:53, disciplinas:[‘Tecnicas de Programacao’, ‘Topicos Avancados’], sexo: ‘m’})

db.professores.find()
Então nosso comando find() nos retornará a lista de todos os registros. Se eu quiser passar alguma cláusula para a busca, posso fazer da seguinte forma:

db.professores.find({nome: ‘Jean’})
//db.professores.find({nome: ‘Jean’, idade:26})
Lembrando que se nossa consulta for falsa, ele não escreverá nenhum resultado, como podemos ver na tela abaixo:

Para fazermos comparações entre valores, usaremos uma sintaxe um pouco diferente. Vamos ter como exemplo a busca por professores com idade acima de 30 anos.

db.professores.find({idade: {$gt:30}})
Como podemos notar com o exemplo, utilizamos um operador condicional o $gt que significa great than, ou maior que, em português.

Também podemos usar os seguintes operadores condicionais: $gte, $lt, $lte, $all, $in, $nin, $ne, $exists, $mod

Ou seja podemos ver que na função find() podemos passar 1 paramêtro que vai fazer a comparacao de valores para delimitar a busca. Tendo em vista que se utilizarmos operadores de comparação como >, >=, <, <= e outros, teremos que usar a seguinte regra:

db.collection.find({campo: {$operador: valor}})

Podemos passar um paramêtro para a comparação de valores, mas também posso passar outro parâmetro, informando quais campos eu quero que retornem na busca; neste caso, nosso exemplo ficará:

db.collection.find({campo: {$operador: valor}}, {campo1: 1, campo2: 1, campoN: 1})
Como visto no exemplo acima ele sempre retorna o _id de cada registro, para que isto não ocorra podemos repassar no paramêtro dos campos _id:0

Porém, caso eu não queira comparar nenhum valor, terei que passar null no primeiro paramêtro, podendo ficar da seguinte maneira:

db.collection.find(null, {campo1: 1, campo2: 1, campoN: 1})

Uma outra função bem útil é a ordenação da busca, que se assemelha ao ORDER BY do SQL. No MongoDB temos a função sort() que recebe um paramêtro para ordernar.

db.collection.find(null, {nome:1, idade:1, _id:0}).sort({idade:1})
Onde passaremos o 1 paras ASC ou seja, do menor para o maior, ou -1 que é o DESC, do maior para menor.

Além de ordenar, também podemos limitar a busca, parecido com o LIMIT do SQL, passando um valor inteiro para a função limit().

db.collection.find(null, {nome:1}).sort({idade:1, nome:-1}).limit(2)

Outra função que auxilia ao limit() é a skip(), que receberá um valor inteiro para pular até aquele registro. Até agora, nossas funções convertidas em SQL ficam assim:

select nome, idade from professores ORDER BY idade ASC LIMIT1,2
Bom, já aprendemos como inserir dados e listá-los esta na hora de modificarmos os registros. Para isto, utilizamos a função update({busca}, {atualizacao}).

Assim como temos o modificador $set, também temos o $unset, que elimina a chave.

db.professores.update({nome: ‘Jean’}, {$unset: {disicplinas: 1}})
Também podemos apenas incrementar ou decrementar um valor inteiro utilizando o modificador $inc

db.professores.update({nome: ‘Jean’}, {$inc: {idade: 5}})
//incrementa em 5 a idade do professor Jean
db.professores.update({nome: ‘Jean’}, {$inc: {idade: -5}})
//incrementa em 5 a idade do professor Jean

Agora para removermos documentos, utilizamos a função remove(json). Onde passamos como parâmetro a clausula de comparação.

Porém, caso queiramos limpar toda a collection, é mais rapido usando drop()

 

 

Instalar MongoDB no Ubuntu

Introdução

Este post demonstra a instalação do MongoDB no Ubuntu.

Procedimento

  1. Importar a chave oficial do MongoDB
    1. sudo apt-key adv –keyserver hkp://keyserver.ubuntu.com:80 –recv 0C49F3730359A14518585931BC711F9BA15703C6
  2. Atualizar a lista de atualizações
    1. sudo apt-get update
  3. Instalando MongoDB
    1. sudo apt-get install mongodb-org
    2. O item 3 não funcionou no meu Ubuntu 17, então substitui o comando para
      1. sudo apt-get install mongodb
  4. Criar pasta /data/db e fornecer permissão de escrita
    1. sudo chmod 775 -R /data
  5. Subindo servidor
    1. sudo mongod
  6. Baixando servidor
    1. sudo mongod stop
  7. Verificando serviço
    1. ps -ef|grep mongo

Angular e Node.js – 1 – Introdução

Olá, tudo bem?

Neste post, vamos iniciar o aprendizado do Angular e Node.js.

Sobre o Angular

AngularJS é um framework JavaScript open-source, mantido pelo Google e pela comunidade, que auxilia na execução de single-page applications.

Sobre o Node.js

Node.js é uma plataforma construída em tempo de execução de JavaScript do Chrome para facilmente construir aplicações de rede rápidos e escalonáveis. Node.js usa um bloqueio não-modelo orientado a eventos, I / O que o torna leve e eficiente, perfeito para aplicações em tempo real de dados intensivos que são executados através de dispositivos distribuídos.

Instalação

Node.js

Para a instalação do Node.js, basta acessar nodejs.org. O site detectará o sistema operacional da máquina e apresentará a correta versão para download. No momento da publicação, a versão disponibilizada foi 8.7.0.

Após a instalação, você pode testar digitando no prompt,

$ npm -version.

Typescript

$ npm install -g typescript

Typescript é um subset do Javascript, que transcompila em diferentes versões do javascript – garantindo mais recursos em ambiente de desenvolvimento e sendo mais portável ao transcompilar para diferentes versões do javascript para a execução.

Angular CLI

$ npm install -g @angular/cli

Verificação: $ ng -v

Trata-se de um facilitador de desenvolvimento de aplicações, ajudando a processar e agrupar arquivos TypeScript, JavaScript, CSS, HTML e imagens.

Criando um projeto

No prompt de comando, vá até o seu diretório onde serão armazenados seus projetos. No meu caso, optei por utilizar /opt/workspaces/projetos-angular/.

Digite o comando $ ng new NOME_DO_PROJETO (utilizei projeto-teste-angular1). Existem regras bem definidas para o nome do projeto, então, fique atento.

Rodando o projeto

Vá até o diretório do projeto (no meu caso, /opt/workspaces/projetos-angular/projeto-teste-angular1) e digite.

$ ng serve

Então, abra um navegador e digite.

http://localhost:4200/

Aparecerá a tela “Welcome to app!”

OutSystems – Exercício 14 – Data Synchronization

Este post demonstra o exercício 14.

Para baixar o material para esse exercício, acesse https://www.outsystems.com/learn/lesson/786/data-synchronization-exercise/

  1. Sincronização Automática
    1. Abra o projeto ToDo
    2. Abra a aba Logic e OfflineDataSyncConfiguration
    3. Mude os parâmetros SyncOnOnline, SyncOnLogin, SyncOnResume e RetryOnError para “Default Value” igual a “True”
    4. Publique a aplicação e rode. Se estiver logado, efetuar logoff e login para visualizar
  2. Habilitando mudanças Offline
    1. Na aba Data (ToDo) criar uma entidade estática SyncStatus
    2. Adicionar registro para None, Added e Updated
    3. Alterar a entidae LocalToDo, adicionando o campo SyncStatusId (relacionado a SyncStatus Identifier)
    4. Modificar a ação SaveOnClick (tela ToDoDetails)
      1. Excluir o If “not GetNetworkStatus()”, pois precisamos gravar localmente, mesmo sem conexão
      2. Criar If para verificação de data de encerramento passada
        1. GetLocalToDoById.List.Current.LocalToDo.DueDate <> NullDate() and GetLocalToDoById.List.Current.LocalToDo.DueDate < CurrDate()
        2. Setar Input_DueDate.Valid = False
        3. Input_DueDate.Message = “Data de encerramento não pode estar no passado”
      3. Criar If de Form1.Valid()
        1. Se Form1.Valid() = False, vá para o nó End
        2. Senão, prosseguir
      4. Criar If Offline (not GetNetworkStatus())
        1. Se verdadeiro, ir para If New To Do?
        2. Se falso
          1. Ir para CreateOrUpdateToDoWrapper (gravação direta no servidor)
          2. Marcar SyncStatus = None
          3. Ir para “CreateOrUpdateLocalToDo
      5. Criar If “New To Do?”
        1. LocalToDoId = NullIdentifier()
        2. Se falso, ir para If “Added SyncStatus?” (verifica se o status do registro está com Added)
        3. Senão
          1. Assign GetLocalToDoById.List.Current.LocalToDo.SyncStatusId = Entities.SyncStatus.Added
          2. Ir para “CreateOrUpdateLocalToDo”
      6. Criar If “Added SyncStatus?”
        1. Condição
          1. GetLocalToDoById.List.Current.LocalToDo.SyncStatusId = Entities.SyncStatus.Added
          2. True, ir para “CreateOrUpdateLocalToDo”
          3. False
            1. assign GetLocalToDoById.List.Current.LocalToDo.SyncStatusId = Entities.SyncStatus.Updated
            2. Ir para “CreateOrUpdateLocalToDo”
      7. Fluxo completo 
    5. Modificar a ação StarOnClick para que fique igual a figura abaixo
    6. Modificar a ação TakePictureOnClick
    7. Modificando ações de sincronização
      1. Na aba Logic, abra “Server Actions” e selecione “ServerDataSync” em “OfflineDataSync”
        1. Adicionar parâmetro “AddedLocalToDos”
        2. Dois cliques em “DataType”
          1. Clique em “Text” e mude para “List”
          2. Clique em tipo “Text” e escolha “Record”
          3. Remova “Text” atributo (ícone de lixeira)
          4. Clique “Add attribute to AddedLocalToDos” e escolha “LocalToDo”
          5. Clique “Add attribute to AddedLocalToDos” e escolha “LocalResource”
          6. Clique em “Close” para fechar a janela
        3. Adicionar outro parâmetro “UpdatedLocalToDos” e repetir o procedimento do item 2
    8. Criando a lógica necessária para ServerDataSync
      1. Abrir o fluxo ServerDataSync (duplo clique)
      2. Tratamento AddedLocalToDos
        1. Adicionar “For Each” entre LogMessage e GetCategories
        2. Adicionar “Run Server Action” e colocar do lado direito do “For Each” e escolher ação de entidade “CreateToDo”
        3. Criar um “Cicle” entre o “For Each” e “CreateToDo”
        4. Selecionar “For Each” e configurar a propriedade “Record List” para “AddedLocalToDos” no parâmetro de entrada
        5. Selecione “CreateToDo” e configure a propriedade Source para “AddedLocalToDos.Current.LocalToDo”
        6. No mapeamento “LocalToDo to ToDo”  configure “Id” para “NullIdentifier()” e “UserId” para “GetUserId()”
        7. Adicionar componente “If” abaixo do “CreateToDo”
          1. Label: “Has Resource?”
          2. Condition: AddedLocalToDos.Current.LocalResource.Id <> NullIdentifier()
        8. Adicione componente “Run Server Action” e colocar à esquerda do “If”
          1. Name: CreateResource
          2. Action: CreateResource
          3. Source: AddedLocalToDos.Current.LocalResource
          4. “Mapping from LocalResource to Resource”
          5. Id: CreateToDo.Id
      3. Tratamento UpdatedLocalToDos
        1. Repetir o procedimento do item 2. / 8. / 2.
        2. Utilizar UpdateLocalToDos no “For Each”
        3. “Run Server Action” “UpdateToDo”
          1. Action: UpdateToDo
          2. Source: UpdateLocalToDos.Current.LocalToDo
          3. “Mapping from LocalToDo to ToDo”
        4. “If” “Has Resource?”
          1. Condition: UpdatedLocalToDos.Current.LocalResource.Id <> NullIdentifier()
        5. “Run Server Action” “CreateOrUpdateResource”
          1. Action: CreateOrUpdateResource
          2. Source: UpdatedLocalToDos.Current.LocalResource
    9. Implementando OfflineDataSync
      1. Em “Client Actions” selecione a ação “OfflineDataSync” (dentro de “OfflineDataSync”)
      2. Adicione um “Aggregate” entre “Start” e “ServerDataSync” e abra (duplo clique)
        1. Arraste a entidade “LocalToDo” para o “Data”
        2. Repita o procedimento acima para a entidade “LocalResource”
        3. Em “Filters” configure: LocalToDo.SyncStatusId = Entities.SyncStatus.Added
        4. Em “Sources” modifique a cláusula entre LocalToDo e LocalResource para “With or Without”
        5. Nomear para “GetAddedLocalToDos”
      3. Repetir o procedimento do item 2, agora para “GetUpdatedLocalToDos”
        1. Em “Filters” configure: LocalToDo.SyncStatusId = Entities.SyncStatus.Updated
      4. Retorne para ação Client “OfflineDataSync”
        1. Configurar o parâmetro AddedLocalToDos para “GetAddedLocalToDos.List”
        2. Configurar o parâmetro UpdatedeLocalToDos para “GetUpdatedLocalToDos.List”
    10. Modificando Offline ícone
      1. Na aba Interface, abra “BottomBar” localizada no fluxo “Common”
      2. Usando “Widget Tree”, localize “BottomBarItem” que contém “Offline” ícone e apague-o
      3. Clique com o botão direito  no “Link” que contém “New To Do” e escolha “Remove Enclosing If”
      4. Arraste um componente “Container” para o mesmo nível do “bottom-bar-ph”
      5. Configure a propriedade “Visible” do “Container como, “not NetworkIsOnline”
      6. Configure no “Container”
        1. Align: Center
        2. Style: “background-light-grey text-italic text-dark-red”
      7. Arraste um componente “Icon” para dentro do “Container” e escolha “Pic an Icon” como “power off”
      8. Configure a propriedade “Size” para “Font Size”
      9. Coloque o cursor ao lado direito do “Icon” e digite: “You are currently offline”
    11. Publique e teste a aplicação

 

Amazon AWS – Redis

O Redis é um armazenamento de estrutura de dados de chave-valor de código aberto e na memória. O Redis oferece um conjunto de estruturas versáteis de dados na memória que permite a fácil criação de várias aplicações personalizadas. Os principais casos de uso do Redis incluem cache, gerenciamento de sessões, PUB/SUB e classificações. É o armazenamento de chave-valor mais conhecido atualmente. Ele tem a licença BSD, é escrito em código C otimizado e é compatível com várias linguagens de desenvolvimento. Redis é um acrônimo de REmote DIctionary Server.

Por conta da sua velocidade e facilidade de uso, o Redis é uma escolha em alta demanda para aplicações web e móveis, como também de jogos, tecnologia de anúncios e IoT, que exigem o melhor desempenho do mercado. A AWS oferece compatibilidade com o Redis por meio de um serviço de banco de dados gerenciado e otimizado chamado Amazon ElastiCache for Redis, além de permitir que os clientes executem o Redis autogerenciado no AWS EC2.

Benefícios

  • Desempenho muito rápido – Todos os dados do Redis residem na memória principal do seu servidor, em contraste com a maioria dos sistemas de gerenciamento de banco de dados que armazenam dados em disco ou SSDs. Ao eliminar a necessidade de acessar discos, bancos de dados na memória, como o Redis, evitam atrasos de tempo de busca e podem acessar dados com algoritmos mais simples que usam menos instruções de CPU. Operações comuns exigem menos do que 10 milissegundos para serem executadas.
  • Estruturas de dados na memória – O Redis permite que os usuários armazenem chaves que fazem o mapeamento para vários tipos de dados. O tipo de dados fundamental é uma string, que pode ser em formato de dados de texto ou binários e ter no máximo 512 MB. O Redis também é compatível com listas de strings na ordem em que foram adicionadas, conjuntos de strings não ordenadas, conjuntos classificados ordenados de acordo com uma pontuação, hashes que armazenam uma lista de campos e valores e HyperLogLogs para contar os itens exclusivos de um conjunto de dados. Praticamente qualquer tipo de dados pode ser armazenado na memória usando o Redis.
  • Versatilidade e facilidade de uso – O Redis é disponibilizado com várias ferramentas que tornam o desenvolvimento e as operações mais rápidas e fáceis, inclusive o PUB/SUB para publicar mensagens nos canais que são entregues para os assinantes, o que é ótimo para sistemas de mensagens e chat, as chaves com TTL podem ter um tempo de vida útil determinado, após a qual elas se autoexcluem, o que ajuda a evitar sobrecarregar o banco de dados com itens desnecessários, os contadores atômicos garantem que condições de corrida não criem resultados incompatíveis, além da Lua, uma linguagem de script potente, porém leve.
  • Replicação e persistência – O Redis emprega uma arquitetura no estilo mestre/subordinado e é compatível com a replicação assíncrona em que os dados podem ser replicados para vários servidores subordinados. Isso pode disponibilizar desempenho de leitura melhorado (à medida que as solicitações podem ser divididas entre os servidores) e recuperação quando o servidor primário passar por uma interrupção. Para disponibilizar durabilidade, o Redis oferece compatibilidade com snapshots point-in-time (copiando o conjunto de dados do Redis no disco) e criando um Append Only File (AOF) para armazenar cada alteração de dados no disco conforme elas vão sendo gravadas. Os dois métodos permitem a restauração rápida dos dados do Redis no caso de uma interrupção.
  • Compatibilidade com a sua linguagem de desenvolvimento preferencial – Mais de cem clientes de código aberto estão disponíveis para os desenvolvedores do Redis. As linguagens compatíveis incluem Java, Python, PHP, C, C++, C#, JavaScript, Node.js, Ruby, R, Go e muitas outras.