Limpar filtro
Artigo
Claudio Devecchi · Fev. 8, 2021
### HealthShare Patient Index
Enterprise Master Patient Index - Este é o nome dado ao processo que faz com que os inúmeros cadastros e registros coletados dos vários sistemas das instituições e redes de saúde sejam identificados univocamente e interligados através de um identificador único por indivíduo.
Isto viabiliza uma infinidade de benefícios para as instituições ou redes de saúde, pois permite, além da gestão das duplicidades em um mesmo sistema de prontuário eletrônico, que todos os dados segregados por número de cadastro sejam visualizados de forma consolidada por indivíduo. Cada vez mais, as instituições estão buscando uma abordagem holística do cuidado contínuo, da prevenção e da experiência centrada no paciente.
Falando sobre o produto [Healthshare Patient Index](https://learning.intersystems.com/course/view.php?id=632) da InterSystems, podemos dividi-lo em 6 grandes grupos de funcionalidades.
### 1 - Integração das informações cadastrais dos pacientes
Esta etapa engloba todos os mecanismos de coleta das informações nos sistemas de origem, seja através de API’s com protocolos específicos de saúde como o HL7, seja através de processos específicos ou customizados.
Neste ponto, é importante que a plataforma de integração ofereça uma série de requisitos de interoperabilidade que assegure flexibilidade, governança e segurança.
É muito comum sistemas de prontuário eletrônico internacionais fornecerem nativamente exportações de dados usando o protocolo HL7, que neste caso também é nativo nos produtos da Intersystems. Os sistemas nacionais geralmente demandam processos menos padronizados, e é por isso que é necessário que a plataforma seja de fácil e rápida implementação.
Geralmente as informações são enviadas ou disponibilizadas no momento em que o paciente é admitido nos estabelecimentos de saúde.
A arquitetura deste processo é definida conforme as necessidades de cada organização e disponibilidade dos recursos computacionais.
### 2 - Análise Qualitativa e Normalização
Normalizar significa trazer para um mesmo plano de comparação informações demográficas que foram cadastradas de formas completamente diferentes. É também nesta etapa que todo o “lixo” é removido. Isto não quer dizer que a informação seja ruim, mas que não serve para o processo do MPI.
Se os dados fossem comparados sem esta etapa, provavelmente cadastros de um mesmo indivíduo nunca seriam comparados, dada à discrepância de sistema para sistema.
Se observarmos o processo de cadastro de cada estabelecimento e sistema de origem, veremos uma infinidade de formas de entrada dos dados e diversas maneiras de armazenamento das informações. Isso depende de como cada sistema foi concebido e como cada processo foi implementado em cada setor da organização.
Um exemplo típico é o processo de admissão em alas emergenciais. Muitas vezes o paciente precisa ser atendido antes mesmo de ser identificado. Isso gera uma série de especificidades que precisam ser tratadas em uma análise qualitativa antes mesmo da implementação do processo de captura dos dados.
O outro exemplo muito comum é o cadastramento de recém nascidos. Cada caso é um caso. Em alguns sistemas os nomes são cadastrados com um prefixo “RN DE” seguido pelo nome da mãe. Isso porque os pais não sabem o nome dos bebês antes do parto e eles já precisam constar nos sistemas de prontuário eletrônico. Como todos os sistemas geralmente exigem o CPF, eles podem ser cadastrados com o mesmo CPF da mãe. É claro que este é só um exemplo de uma situação pontual, mas cada caso deve ser estudado e endereçado da forma mais adequada possível.
Além das especificidades de processo, há as que são de armazenamento dos dados. Documentos como CPF, RG e carteirinhas de seguro são armazenados com pontos e traços em alguns sistemas. Em outros são armazenados sem. O mesmo ocorre com datas. Nomes geralmente são armazenados em um único campo, uns com caixa baixa, outros com alta. Alguns são abreviados pela limitação de caracteres.
Endereços são os vilões na normalização. Os sistemas mais modernos são baseados no CEP, outros não. Os que não são sofrem muitas abreviações devidos aos sufixos, títulos ou até mesmo pela limitação dos caracteres.
Enfim, mesmo que em instituições mais modernas tecnologicamente, há sempre os sistemas legados. Estes também são incorporados ao processo de MPI porque trazem informações históricas valiosas para todo o processo assistencial.
Culturalmente e diferentemente dos sistemas norte americanos, os sistemas brasileiros possuem um único campo para capturar os nomes. Para melhorar a eficácia do processo de vinculação é importante que os sistemas tenham a capacidade de separar os nomes, considerando também os primeiros nomes compostos.
Outra capacidade não menos importante é a capacidade de trabalhar com as abreviações nos endereços. Isto requer um dicionário específico de abreviações para o nosso país.
### 3 - Indexação ou formação dos pares de comparação
Nesta etapa é que se decide quais serão os cadastros que serão comparados entre si, formando assim os chamados pares de comparação.
A decisão de se comparar registros não se baseia apenas nos documentos do paciente, assim como o CPF. Isso acontece porque há casos que não se tem o CPF do paciente ou casos que os filhos recebem o CPF dos pais. Para isto é necessário que o processo utilize os dados probabilísticos, assim como o nome, a data de nascimento, o sexo, os dados de contato e o endereço.
É preciso que este processo tenha algoritmos sofisticados para que os sistemas não gerem um número excessivo de comparações indevidas, assim como não deixem de fora comparações necessárias.
Por exemplo: A comparação de todos os “Josés” com todos os outros “Josés” não seria tão eficaz, pois poderia acarretar numa sobrecarga de processamento.
Outro ponto não menos importante nesta fase é a capacidade de se trabalhar com algoritmos fonéticos para o mercado brasileiro, que são completamente diferentes dos algoritmos americanos.
Isso significa que nomes escritos de maneiras diferentes ou equivocadas também serão considerados no processo. Exemplo: Dois cadastros de um determinado paciente com os nomes Walter Xavier e Valter Chavier podem se referir ao mesmo indivíduo.
O Healthshare MPI utiliza um processo extremamente eficiente de análise combinatória que evita este tipo de problema, utilizando tanto informações demográficas determinísticas quanto probabilísticas.
### 4 - Pontuação dos pares
Para cada par de comparação gerado, todas as variáveis demográficas são pontuadas separadamente: Primeiros nomes, nomes do meio, sobrenomes, documentos, sexo, cep, telefones, e-mails e endereços.
Antes de iniciar a comparação, é determinado um peso com uma pontuação máxima e mínima para cada variável, considerando a singularidade de cada uma. Por exemplo, o sexo possui um peso menor que a data de nascimento, que possui peso menor que o nome, que possui um peso menor que o CPF. E assim por diante.
Cada variável possui um algoritmo específico não binário de comparação que vai atribuir uma pontuação entre a mínima e a máxima para cada variável demográfica.
Exemplo: Se o sexo for o mesmo, serão atribuídos 2 pontos. Se não for o mesmo será atribuída a pontuação mínima, -4 pontos. Se o CPF for o mesmo, serão atribuídos 14 pontos,
Primeiros nomes e sobrenomes comuns também recebem pontuações menores que nomes mais comuns, assim como Silva e Souza.
Nomes de casada e solteira também devem ser considerados aqui no Brasil.
### 5 - Avaliação e determinação do identificador unívoco dos pares
Antes desta etapa, é necessário configurar as faixas de pontuação ou limiares que serão utilizados para vincular (mesmo indivíduo) ou não vincular (indivíduos diferentes) os pares de cadastro.
Neste ponto, pode-se definir também a faixa de pontuação dos pares que irão para uma lista de trabalho, passíveis de uma avaliação ou revisão humana.
Limiares a serem configurados:
**Vínculo Automático** – Acima de quantos pontos os pares serão automaticamente vinculados. Exemplo: Se o total de pontos dos pares estiver acima de 35 os mesmos serão automaticamente vinculados e não necessitarão de revisão humana.
**Vínculo com posterior avaliação na Lista de Trabalho** – Entre quantos pontos os pares irão para a lista de trabalho como vinculados (mesmos indivíduos) para avaliação humana. Exemplo: Os pares entre 30 e 35 pontos serão vinculados, mas poderão sofrer revisão de um profissional ou equipe designados para esta tarefa.
**Não vínculo** – Abaixo de quantos pontos os pares não serão vinculados. Exemplo: Se o total de pontos dos pares for abaixo de 30 eles não serão vinculados (indivíduos diferentes).
**Não vínculo com revisão posterior na Lista de Trabalho** – Entre quantos pontos os pares irão para a lista de trabalho como não vinculado (indivíduos diferentes) para uma revisão humana. Exemplo: Os pares entre 25 e 30 pontos não serão vinculados, mas poderão sofrer revisão de um profissional ou equipe designados para esta tarefa.
Há várias situações de exceções, onde mesmo pares com pontuação elevada podem não se referir ao mesmo indivíduo. Um exemplo típico são os gêmeos, que moram na mesma residência. Para isso é necessário que o produto disponibilize de artifícios para identificar estes casos.
Há outras situações que pares com baixa pontuação podem sofrer revisões se determinadas situações ocorrerem. Exemplo: pares com o mesmo CPF e data de nascimento e baixa pontuação. Este caso é no mínimo curioso, pois pode apontar um problema na baixa qualidade dos dados.
No HealthShare Patient Index, estes dispositivos são chamados de regras de vinculação (rules), que prevalecem sobre a regra de pontuação.
O produto já possui nativamente uma série de regras de exceção e elas são fundamentais para a segurança e confiabilidade de todo o processo.
Após esta etapa, todos os cadastros recebem um identificador universal denominado MPIID - Master Patient Index Identification. Os cadastros que possuírem o mesmo MPIID são referentes ao mesmo indivíduo.
### 6 – Serviços e API’s ###
Concluindo todo o processo de vinculação (Matching), é essencial que a plataforma ofereça maneiras passivas ou ativas de se comunicarem ou interoperarem com os sistemas de origem ou outros sistemas. Neste momento entram novamente todos os requisitos de interoperabilidade do produto, que já estão presentes na plataforma HealthShare da Intersystems.
As API’s de consumo do MPI são disponibilizadas neste momento através de protocolos conhecidos (HTTP Soap ou Rest) para que sistemas consigam obter as informações desejadas para diversos casos de uso.
Estes são alguns exemplos comuns de consumo de API’s do HealthShare MPI:
• Obter identificadores de outros sistemas partindo do identificador do sistema consumidor. Este tipo de consulta é denominada pelo IHE como PIX. Exemplo: Antes de enviar a prescrição para o laboratório o sistema de origem envia o seu identificador do cadastro e recebe uma resposta da API com o número do identificador do mesmo paciente no laboratório.
• Realizar pesquisas probabilísticas por dados demográficos. Exemplo: Consultar se existem cadastros demográficos para o paciente de nome Claudio Devecchi Junior. Este tipo de consulta é denominada pelo IHE como PDQ).
• Obter o melhor dado demográfico (Golden Record ou Composite Record) de um determinado paciente para enriquecer os cadastros demográficos ou para aproveitar os seus dados no momento de um determinado cadastro.
Existem também mecanismos ativos, onde o MPI se comunica com os sistemas para enviar informações úteis. Estes mecanismos também podem ser acionados de forma passiva através da chamada das Api’s.
Alguns exemplos são:
• No momento que um cadastro está sendo incluído ou atualizado o MPI pode fazer uma chamada retornando o identificador universal - MPIID. Desta forma o sistema de origem sempre ficará atualizado com este identificador. Quaisquer mudanças nas Listas de Trabalho são gatilhos para este tipo de chamada (callback)
• Quando algum cadastro for incluído e o MPI identificar que já existe esta mesma pessoa no mesmo sistema de origem, já é possível enviar uma notificação de duplicidade. Para os casos de resolução das duplicidades, é importante que exista um serviço específico para receber as mensagens de fusão de pacientes (PIX merge).
### Conclusão
Todo o processo descrito anteriormente demonstra um pouco de como o produto HealthShare Patient Index trata dos desafios na área com relação aos cadastros e identificação dos pacientes e como é importante tratar das especificidades não somente do país, mas de organização para organização.
No próximo artigo, falaremos um pouco de como funciona o Healthshare Unified Care Record (UCR) e de como ele é fundamental para ajudar as instituições na abordagem holística do cuidado contínuo, da prevenção e da experiência centrada no paciente.
Artigo
Heloisa Paiva · Abr. 28
Tenho um novo projeto para armazenar informações de respostas REST em um banco de dados IRIS. Precisarei sincronizar informações de pelo menos duas dúzias de endpoints REST separados, o que significa criar quase essa quantidade de classes ObjectScript para armazenar os resultados desses endpoints.
Poderia usar o ChatGPT para ter uma vantagem inicial na criação dessas classes? A resposta é "Sim", o que é ótimo, já que esta é minha primeira tentativa de usar IA generativa para algo útil. Gerar fotos de girafas tomando sopa estava ficando meio batido...
Aqui está o que eu fiz:
Obter alguma saída de chamada REST JSON de amostra usando "curl" para consultar o endpoint.
Visitar o ChatGPT (a InterSystems fornece uma licença para toda a empresa para os funcionários)
Tenha o seguinte diálogo com o ChatGPT.
Coloquei o seguinte pedido no prompt "Message ChatGPT":
Eu: Gere uma classe ObjectScript que armazenará dados do seguinte JSON:
Apertei "Enter" antes de inserir o JSON, então o ChatGPT me direcionou da seguinte forma:
ChatGPT: Por favor, forneça a estrutura JSON para a qual você gostaria que eu gerasse a classe ObjectScript. Se você tiver um objeto JSON de exemplo, cole-o aqui e eu criarei a classe InterSystems ObjectScript correspondente para você.
Eu inseri o seguinte no prompt:
{
"id": 335818632,
"appId": 113,
"appName": "Batch",
"contentId": 182455380233,
"subject": "Test Subject",
"name": "Test email campaign name",
"counters": {
"processed": 3910,
"deferred": 86,
"unsubscribed": 17,
"statuschange": 17,
"bounce": 153,
"mta_dropped": 8,
"dropped": 299,
"delivered": 3749,
"sent": 3903,
"click": 16,
"open": 323
},
"lastProcessingFinishedAt": 1733232499000,
"lastProcessingStartedAt": 1733232303000,
"lastProcessingStateChangeAt": 1733232499000,
"numIncluded": 4201,
"processingState": "DONE",
"scheduledAt": 1733232300000,
"type": "BATCH_EMAIL"
}
Resposta do ChatGPT:
Aqui está a classe InterSystems ObjectScript que mapeia a estrutura JSON fornecida:
Class MyNamespace.MyJSONData Extends (%Persistent, %JSON.Adaptor)
{
Property id As %Integer;
Property appId As %Integer;
Property appName As %String;
Property contentId As %Integer;
Property subject As %String;
Property name As %String;
Property counters As MyNamespace.Counters;
Property lastProcessingFinishedAt As %BigInt;
Property lastProcessingStartedAt As %BigInt;
Property lastProcessingStateChangeAt As %BigInt;
Property numIncluded As %Integer;
Property processingState As %String;
Property scheduledAt As %BigInt;
Property type As %String;
ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyNamespace.MyJSONData) As %Status
{
Quit ..%JSONImport(jsonObject, .instance)
}
}
Class MyNamespace.Counters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As %Integer;
Property deferred As %Integer;
Property unsubscribed As %Integer;
Property statuschange As %Integer;
Property bounce As %Integer;
Property mta_dropped As %Integer;
Property dropped As %Integer;
Property delivered As %Integer;
Property sent As %Integer;
Property click As %Integer;
Property open As %Integer;
}
Junto com uma explicação:
Explicação:
Mynamespace.MyJSONData: A principal classe representando o objeto JSON.
As propriedades mapeiam diretamente para os campos no JSON.
Ela estende tanto %Persistent quanto %JSON.Adaptor para persistência e compatibilidade com JSON.
Mynamespace.Counters: Uma classe aninhada para representar o objeto counters no JSON.
Esta classe é definida como um %SerialObject, pois faz parte do objeto principal e não precisa de persistência independente.
Manipulação de JSON:
O %JSON.Adaptor lida automaticamente com a serialização e desserialização de/para o formato JSON.
O método %OnJSONImport permite a personalização da importação JSON, embora o método padrão %JSONImport seja suficiente para esse mapeamento direto.
Você pode salvar esse código em um arquivo de classe e usá-lo para analisar ou armazenar dados JSON. Avise se quiser recursos adicionais ou mais personalizações!
Este código é um ótimo começo, mas eu gostaria que ele se encaixasse no meu pacote chamado“MyPkg”e desse o nome "EmailCampaign" para a classe. Além disso, usando um pouco de NI (Network Intelligence), sei que os valores que geraram as propriedades "%BigInt" são, na verdade, tempos Linux e as propriedades deveriam ser "%Library.PosixTime".
Faço o seguinte pedido (não tenho certeza por que estou dizendo "por favor" para uma máquina; acho que meus pais fizeram um bom trabalho):
Eu: Podemos recriar a classe novamente, renomeando o pacote para "MyPkg" e a Classe como "EmailCampaign"? Além disso, por favor, altere os tipos das propriedades de %BigInt para %Library.PosixTime.
Resposta do ChatGPT:
Aqui está a classe ObjectScript atualizada com o nome do pacote e o nome da classe especificados, e as propriedades de timestamp atualizadas para usar %Library.PosixTime:
Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As %Integer;
Property appId As %Integer;
Property appName As %String;
Property contentId As %Integer;
Property subject As %String;
Property name As %String;
Property counters As MyPkg.Counters;
Property lastProcessingFinishedAt As %Library.PosixTime;
Property lastProcessingStartedAt As %Library.PosixTime;
Property lastProcessingStateChangeAt As %Library.PosixTime;
Property numIncluded As %Integer;
Property processingState As %String;
Property scheduledAt As %Library.PosixTime;
Property type As %String;
ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyPkg.EmailCampaign) As %Status
{
Quit ..%JSONImport(jsonObject, .instance)
}
}
Class MyPkg.Counters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As %Integer;
Property deferred As %Integer;
Property unsubscribed As %Integer;
Property statuschange As %Integer;
Property bounce As %Integer;
Property mta_dropped As %Integer;
Property dropped As %Integer;
Property delivered As %Integer;
Property sent As %Integer;
Property click As %Integer;
Property open As %Integer;
}
Junto com esta explicação:
Alterações Feitas:
Nome do Pacote e da Classe:
O pacote agora é CRMBI.
A classe principal foi renomeada para EmailCampaign.
Propriedades de Timestamp:
lastProcessingFinishedAt, lastProcessingStartedAt, lastProcessingStateChangeAt e scheduledAt agora utilizam %Library.PosixTime.
%Library.PosixTime é apropriado para propriedades que armazenam timestamps no formato UNIX.
Outras Propriedades:
Todos os outros campos permanecem inalterados e seguem a estrutura original do JSON.
Esta classe está pronta para lidar com a serialização/desserialização JSON e fornece o mapeamento correto para o objeto aninhado counters. Me avise se precisar de mais ajustes ou tiver novos requisitos!
Depois de copiar o código "como está" para o VSCode, problemas de sintaxe com as chaves foram destacados. Classes ObjectScript não habilitam definições de classe internas.
Pedir ao ChatGPT para corrigir o problema das "chaves" movendo a classe interna MyPkg.Counters para uma classe separada é uma "não-operação"; um humano precisará mover essa classe para outro arquivo. O ChatGPT renomeou essa classe após o seguinte pedido:
Eu: A classe como está escrita não compila. Podemos mover a classe interna chamada MyPkg.Counters para fora da classe principal para sua própria classe e renomeá-la para MyPkg.CampaignCounters?
Resposta do ChatGPT:
Aqui está o código ObjectScript atualizado com a classe MyPkg.CampaignCounters movida para fora como sua própria classe:
Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As %Integer;
Property appId As %Integer;
Property appName As %String;
Property contentId As %Integer;
Property subject As %String;
Property name As %String;
Property counters As MyPkg.CampaignCounters;
Property lastProcessingFinishedAt As %Library.PosixTime;
Property lastProcessingStartedAt As %Library.PosixTime;
Property lastProcessingStateChangeAt As %Library.PosixTime;
Property numIncluded As %Integer;
Property processingState As %String;
Property scheduledAt As %Library.PosixTime;
Property type As %String;
ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyPkg.EmailCampaign) As %Status
{
Quit ..%JSONImport(jsonObject, .instance)
}
}
Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As %Integer;
Property deferred As %Integer;
Property unsubscribed As %Integer;
Property statuschange As %Integer;
Property bounce As %Integer;
Property mta_dropped As %Integer;
Property dropped As %Integer;
Property delivered As %Integer;
Property sent As %Integer;
Property click As %Integer;
Property open As %Integer;
}
Depois de colar o código acima no VSCode e compilá-lo, o compilador relatou que o tipo chamado "%Object" não existe em ObjectScript. De acordo com a documentação do IRIS, a propriedade para o primeiro parâmetro do método %JSONImport() de %JSON.Adaptor é flexível:
“O argumento de entrada é JSON como uma string ou stream, ou uma subclasse de %DynamicAbstractObject.”
Depois de remover a propriedade "As %Object" para o primeiro parâmetro de %OnJSONImport() na primeira classe e separar as duas classes em arquivos separados, a classe MyPkg.EmailCampaign compilou com sucesso. No entanto, a MyPkg.CampaignCounters não compilou; o ChatGPT não sabe (ainda?) que o sublinhado é um caractere especial em ObjectScript (o operador de concatenação de strings)
Vamos tentar ensiná-lo.
Eu: O caractere sublinhado "_" é um caractere especial em ObjectScript. Por favor, altere as propriedades com um sublinhado em seus nomes para nomes em camel case e remova o sublinhado.
Resposta do ChatGPT:
Aqui está o código atualizado com as propriedades que tinham sublinhados em seus nomes convertidas para camel case:
Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As %Integer;
Property appId As %Integer;
Property appName As %String;
Property contentId As %Integer;
Property subject As %String;
Property name As %String;
Property counters As MyPkg.CampaignCounters;
Property lastProcessingFinishedAt As %Library.PosixTime;
Property lastProcessingStartedAt As %Library.PosixTime;
Property lastProcessingStateChangeAt As %Library.PosixTime;
Property numIncluded As %Integer;
Property processingState As %String;
Property scheduledAt As %Library.PosixTime;
Property type As %String;
ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyPkg.EmailCampaign) As %Status
{
Quit ..%JSONImport(jsonObject, .instance)
}
}
Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As %Integer;
Property deferred As %Integer;
Property unsubscribed As %Integer;
Property statusChange As %Integer;
Property bounce As %Integer;
Property mtaDropped As %Integer;
Property dropped As %Integer;
Property delivered As %Integer;
Property sent As %Integer;
Property click As %Integer;
Property open As %Integer;
}
Junto com esta explicação:
Mudanças Realizadas:
1. Conversão para Camel Case:
Propriedades em CampaignCounters com underscores agora são escritas em camel case:
statuschange → statusChange
mta_dropped → mtaDropped
2. Nomes de Propriedades em JSON:
ObjectScript irá mapear automaticamente nomes de propriedades em camel case para seus correspondentes com underscore no JSON usando o %JSON.Adaptor.
Este código atualizado compila corretamente e preserva a funcionalidade para serialização/desserialização JSON! Avise-me se precisar de mais ajustes!
O ChatGPT foi além e alterou outra propriedade, uma nomeada com duas palavras separadas, para camel case também (statuschange); estou mantendo essa alteração, já que outras propriedades com nomes semelhantes na classe principal estão todas em camel case.
Com o código gerado mais recente em dois arquivos separados (e removendo a definição "As %Object" na definição do método %OnJSONImport()), todo o código compila com sucesso.
Essas classes geradas são um excelente ponto de partida para o que preciso e repetirei o processo para as outras fontes de dados REST para este projeto, me poupando muita digitação desnecessária.
Artigo
Danusa Calixto · Jul. 27, 2022
**Histórico**
| Versão| Data | Atualizações|
| :----- | :-- | :----- |
| V1 | 2022-02-08 | Início |
| V1.1 | 2022-04-06 | Geração de certificados com arquivo sh em vez de pki-scriptUsando variáveis de ambiente em arquivos de configuração|
Olá Comunidade,
Vocês já configuraram um ambiente espelhado? Tem rede privada, IP virtual, e configuração SSL?
Após fazer isso algumas vezes, eu me dei conta que isso é longo, e há várias ações manuais obrigatórias para gerar os certificados e configurar cada instancia IRIS.
É uma dor no pescoço para quem tem que fazer isso muitas vezes.
Por exemplo, um time de Quality Assurance pode precisar criar um novo ambiente para cada nova versão da aplicação para testar. O time de suporte pode exigir a criação de um ambiente para reproduzir um problema complexo.
Nós definitivamente precisamos de ferramentas para criá-los rapidamente.
Neste artigo iremos criar um exemplo para configurar um Mirror com:
- Arbitro
- Primário
- Membro de backup
- Membro assíncrono de relatorio (leitura e escrita)
- Configuração SSL para transferencia de Journal entre os nós
- Rede privada para o Mirror
- Endereço IP virtual
- Um banco de dados espelhado

À primeira vista, parece um pouco complexo e parece que precisa de muito código, mas não se preocupe.
Existem bibliotecas hospedadas no OpenExchange para realizar facilmente a maioria das operações.
O objetivo deste artigo é fornecer um exemplo de como adaptar o processo às suas necessidades, mas não é um guia de práticas recomendadas em termos de segurança.
Então, vamos criar nossa amostra.
### Ferramentas e bibliotecas
- [config-api](https://openexchange.intersystems.com/package/Config-API): Esta biblioteca será utilizada para configurar o IRIS. Ela suporta configuração de espelhamento desde a versão 1.1.0. Não iremos dar uma descrição detalhada de como usar esta biblioteca. Já existe uma série de artigos. [here](https://community.intersystems.com/post/environment-setup-config-api). Resumindo, config-api será usada para criar um modelo de arquivos de configuração IRIS (formato JSON) e carregá-los facilmente.
- [ZPM](https://openexchange.intersystems.com/package/ObjectScript-Package-Manager).
- Docker.
- OpenSSL.
### Github
Você pode encontrar todos os arquivos necessários em [iris-mirroring-samples repository](https://github.com/lscalese/iris-mirroring-samples/).
### Prepare o seu sistema
Clone o repositorio existente:
```bash
git clone https://github.com/lscalese/iris-mirroring-samples
cd iris-mirroring-samples
```
Se você preferir criar uma amostra do zero, ao invés de clonar o repositório, basta criar um novo diretório com subdiretórios: `backup` e `config-files`. Baixe
[irissession.sh](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh) :
```
mkdir -p iris-mirroring-samples/backup iris-mirroring-samples/config-files
cd iris-mirroring-samples
wget -O session.sh https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh
```
Para evitar o problema de "permissão negada" posteriormente, precisamos criar o grupo 'irisowner', o usuário 'irisowner', e alterar o grupo do diretório de backup para 'irisowner'
```bash
sudo useradd --uid 51773 --user-group irisowner
sudo groupmod --gid 51773 irisowner
sudo chgrp irisowner ./backup
```
Este diretório será utilizado como volume para compartilhar um backup do banco de dados após configurar o primeiro membro espelho com os outros nós.
### Obtenha uma licença IRIS
O espelhamento não está disponível com a edição da comunidade IRIS.
Se você ainda não tiver uma licença de contêiner IRIS válida, conecte-se ao [Worldwide Response Center (WRC)](https://wrc.intersystems.com) com suas credenciais.
Clique em "Ações" --> "Distribuição online", depois no botão "Avaliações" e selecione "Licença de avaliação"; Preencha o formulário.
Copie seu arquivo de licença `iris.key` para este diretório.
### Login no Intersystems Containers Registry
Por motivos de conveniência, usamos o Intersystems Containers Registry (ICR) para extrair imagens do docker. Se você não souber seu login\\password do docker, basta conectar-se a [SSO.UI.User.ApplicationTokens.cls](https://login.intersystems.com/login/SSO.UI.User.ApplicationTokens.cls) com suas credenciais WRC, e você pode recuperar seu Token ICR.
```bash
docker login -u="YourWRCLogin" -p="YourICRToken" containers.intersystems.com
```
### Crie o banco de dados `myappdata` e o mapeamento de global
Nós não criamos realmente o banco de dados `myappdata` agora, mas preparamos uma configuração para criá-lo no momento da compilação do docker.
Para isso, basta criar um arquivo simples usando o formato JSON;
A biblioteca config-api será usada para carregá-la nas instâncias IRIS.
Crie o arquivo [config-files/simple-config.json](https://github.com/lscalese/iris-mirroring-samples/blob/master/config-files/simple-config.json)
```json
{
"Defaults":{
"DBDATADIR" : "${MGRDIR}myappdata/",
"DBDATANAME" : "MYAPPDATA"
},
"SYS.Databases":{
"${DBDATADIR}" : {}
},
"Databases":{
"${DBDATANAME}" : {
"Directory" : "${DBDATADIR}"
}
},
"MapGlobals":{
"USER": [{
"Name" : "demo.*",
"Database" : "${DBDATANAME}"
}]
},
"Security.Services" : {
"%Service_Mirror" : { /* Habilita o serviço de espelhamento nesta instancia */
"Enabled" : true
}
}
}
```
Este arquivo de configuração permite que você crie um novo banco de dados com configurações padrão e faça o mapeamento global `demo.*` no namespace USER.
Para obter mais informações sobre os recursos do arquivo de configuração [config-api](https://openexchange.intersystems.com/package/config-api), consulte o [artigo](https://community.intersystems.com/post/environment -setup-config-api) ou a [página do github](https://community.intersystems.com/post/environment-setup-config-api)
### O arquivo Docker
O arquivo docker é baseado no [modelo docker] existente (https://github.com/intersystems-community/objectscript-docker-template), mas precisamos fazer algumas alterações para criar um diretório de trabalho, instalar ferramentas para usar IP, instalar ZPM, etc…
Nossa imagem IRIS é a mesma para cada membro do espelho. O espelhamento será configurado no contêiner começando com a configuração correta dependendo de sua função (primeiro membro, backup ou relatório de leitura/gravação). Veja os comentários sobre o Dockerfile abaixo:
```Dockerfile
ARG IMAGE=containers.intersystems.com/intersystems/iris:2021.1.0.215.0
# Não precisa baixar a imagem do WRC. Ele será retirado do ICR no momento da construção.
FROM $IMAGE
USER root
COPY session.sh /
COPY iris.key /usr/irissys/mgr/iris.key
# /opt/demo será nosso diretório de trabalho usado para armazenar nossos arquivos de configuração e outros arquivos de instalação.
# Instale o iputils-arping para ter um comando arping. É necessário configurar o IP virtual.
# Baixe a versão mais recente do ZPM (o ZPM está incluído apenas na edição da comunidade).
RUN mkdir /opt/demo && \
chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/demo && \
chmod 666 /usr/irissys/mgr/iris.key && \
apt-get update && apt-get install iputils-arping gettext-base && \
wget -O /opt/demo/zpm.xml https://pm.community.intersystems.com/packages/zpm/latest/installer
USER ${ISC_PACKAGE_MGRUSER}
WORKDIR /opt/demo
# Defina a função de espelho padrão como mestre.
# Ele será substituído no arquivo docker-compose em tempo de execução (mestre para a primeira instância, backup e relatório)
ARG IRIS_MIRROR_ROLE=master
# Copie o conteúdo do diretório config-files em /opt/demo.
# Atualmente criamos apenas uma configuração simples para configurar nosso banco de dados e mapeamento de global.
# Mais adiante neste artigo adicionaremos outros arquivos de configuração para configurar o espelho.
ADD config-files .
SHELL [ "/session.sh" ]
# Instale o ZPM
# Use ZPM para instalar config-api
# Carregue o arquivo simple-config.json com config-api para:
# - criar o banco de dados "myappdata" ,
# - adicionar o mapeamento de global no namespace "USER" para a global "demo.*" no banco de dados "myappdata" .
# Basicamente, o ponto de entrada para instalar seu aplicativo ObjectScript é aqui.
# Para este exemplo, carregaremos o simple-config.json para criar um banco de dados simples e um mapeamento de global.
RUN \
Do $SYSTEM.OBJ.Load("/opt/demo/zpm.xml", "ck") \
zpm "install config-api" \
Set sc = ##class(Api.Config.Services.Loader).Load("/opt/demo/simple-config.json")
# Copie o script de instalação do espelho
COPY init_mirror.sh /
```
### Construa a imagem IRIS
O Dockerfile está pronto; podemos construir a imagem:
```
docker build --no-cache --tag mirror-demo:latest .
```
Essa imagem será usada para executar nós primários, de backup e de relatório.
### O arquivo .env
Os arquivos de configuração JSON e a composição do docker usam variáveis de ambiente.
Seus valores são armazenados em um arquivo chamado `.env`, para este exemplo nosso arquivo env é:
```
APP_NET_SUBNET=172.16.238.0/24
MIRROR_NET_SUBNET=172.16.220.0/24
IRIS_HOST=172.16.238.100
IRIS_PORT=1972
IRIS_VIRTUAL_IP=172.16.238.100
ARBITER_IP=172.16.238.10
MASTER_APP_NET_IP=172.16.238.20
MASTER_MIRROR_NET_IP=172.16.220.20
BACKUP_APP_NET_IP=172.16.238.30
BACKUP_MIRROR_NET_IP=172.16.220.30
REPORT_APP_NET_IP=172.16.238.40
REPORT_MIRROR_NET_IP=172.16.220.40
```
### Prepare o primeiro arquivo de configuração do membro espelho
A biblioteca config-api permite configurar um espelho, então temos que criar um arquivo de configuração dedicado ao primeiro membro do espelho `config-files/mirror-master.json`
Por conveniência, os comentários estão localizados diretamente no JSON. Você pode baixar o [mirror-master.json sem comentários aqui](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/config-files/mirror-master.json).
```json
{
"Security.Services" : {
"%Service_Mirror" : {
"Enabled" : true
}
},
"SYS.MirrorMaster" : {
"Demo" : {
"Config" : {
"Name" : "Demo", /* O nome do nosso espelho */
"SystemName" : "master", /* O nome da instancia no espelhamento */
"UseSSL" : true,
"ArbiterNode" : "${ARBITER_IP}|2188", /* Endereço IP e Porta do nó do Arbitro */
"VirtualAddress" : "${IRIS_VIRTUAL_IP}/24", /* Endereço IP virtual */
"VirtualAddressInterface" : "eth0", /* Interface de rede usada para o endereço de IP virtual */
"MirrorAddress": "${MASTER_MIRROR_NET_IP}", /* Endereço IP deste nó na rede privada do espelho */
"AgentAddress": "${MASTER_APP_NET_IP}" /* Endereço IP deste nó (o Agente é istalado na mesma máquina) */
},
"Databases" : [{ /* Lista de banco de dados para adicionar ao espelhamento */
"Directory" : "/usr/irissys/mgr/myappdata/",
"MirrorDBName" : "MYAPPDATA"
}],
"SSLInfo" : { /* SSL Configuration */
"CAFile" : "/certificates/CA_Server.cer",
"CertificateFile" : "/certificates/master_server.cer",
"PrivateKeyFile" : "/certificates/master_server.key",
"PrivateKeyPassword" : "",
"PrivateKeyType" : "2"
}
}
}
}
```
### Prepare o arquivo de configuração do membro de failover
Crie um arquivo de configuração do membro de backup de failover `config-files/mirror-backup.json`.
Parece o primeiro membro:
```json
{
"Security.Services" : {
"%Service_Mirror" : {
"Enabled" : true
}
},
"SYS.MirrorFailOver" : {
"Demo" : { /* Espelho para juntar */
"Config": {
"Name" : "Demo",
"SystemName" : "backup", /* Este nome de instância no espelho */
"InstanceName" : "IRIS", /* Nome da instancia IRIS do primeiro membro do espelho */
"AgentAddress" : "${MASTER_APP_NET_IP}", /* Endereço IP do agente do primeiro membro espelho */
"AgentPort" : "2188",
"AsyncMember" : false,
"AsyncMemberType" : ""
},
"Databases" : [{ /* DB no espelho */
"Directory" : "/usr/irissys/mgr/myappdata/"
}],
"LocalInfo" : {
"VirtualAddressInterface" : "eth0", /* Interface de rede usada para o endereço IP virtual. */
"MirrorAddress": "${BACKUP_MIRROR_NET_IP}" /* Endereço IP deste nó na rede espelho privada*/
},
"SSLInfo" : {
"CAFile" : "/certificates/CA_Server.cer",
"CertificateFile" : "/certificates/backup_server.cer",
"PrivateKeyFile" : "/certificates/backup_server.key",
"PrivateKeyPassword" : "",
"PrivateKeyType" : "2"
}
}
}
}
```
### Prepare o arquivo de configuração do membro assíncrono de leitura e gravação
É bastante semelhante ao arquivo de configuração de failover. As diferenças são os valores de `AsyncMember`, `AsyncMemberType` e `MirrorAddress`.
Crie o arquivo `./config-files/mirror-report.json`:
```json
{
"Security.Services" : {
"%Service_Mirror" : {
"Enabled" : true
}
},
"SYS.MirrorFailOver" : {
"Demo" : {
"Config": {
"Name" : "Demo",
"SystemName" : "report",
"InstanceName" : "IRIS",
"AgentAddress" : "${MASTER_APP_NET_IP}",
"AgentPort" : "2188",
"AsyncMember" : true,
"AsyncMemberType" : "rw"
},
"Databases" : [{
"Directory" : "/usr/irissys/mgr/myappdata/"
}],
"LocalInfo" : {
"VirtualAddressInterface" : "eth0",
"MirrorAddress": "${REPORT_MIRROR_NET_IP}"
},
"SSLInfo" : {
"CAFile" : "/certificates/CA_Server.cer",
"CertificateFile" : "/certificates/report_server.cer",
"PrivateKeyFile" : "/certificates/report_server.key",
"PrivateKeyPassword" : "",
"PrivateKeyType" : "2"
}
}
}
}
```
### Gere os certificados e configure os nós IRIS
Todos os arquivos de configuração estão prontos!
Agora temos que adicionar script para gerar certificados para comunicação segura entre cada nós. Um script pronto para uso está disponível no repositório [gen-certificates.sh](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/gen-certificates.sh)
```
# sudo é necessário devido ao uso de chown, chgrp chmod.
sudo ./gen-certificates.sh
```
Para configurar cada nó o `init_mirror.sh` será executado no início dos containers. Ele será configurado posteriormente em `docker-compose.yml` na seção de comando `command: ["-a","/init_mirror.sh"]` :
```bash
#!/bin/bash
# Banco de dados usado para testar o espelho.
DATABASE=/usr/irissys/mgr/myappdata
# O diretório contém myappdata com backup feito pelo mestre para restaurar em outros nós e fazer o espelho.
BACKUP_FOLDER=/opt/backup
# Espelhar arquivo de configuração no formato json config-api para o nó mestre.
MASTER_CONFIG=/opt/demo/mirror-master.json
# Espelhar arquivo de configuração no formato json config-api para o nó de backup.
BACKUP_CONFIG=/opt/demo/mirror-backup.json
# Espelhar arquivo de configuração no formato json config-api para o nó assíncrono do relatório.
REPORT_CONFIG=/opt/demo/mirror-report.json
# O nome do espelho
MIRROR_NAME=DEMO
# Lista de membros do espelho .
MIRROR_MEMBERS=BACKUP,REPORT
# Realizado no mestre.
# Carregue a configuração do espelho usando config-api com o arquivo /opt/demo/simple-config.json.
# Inicie um Job para aceitar automaticamente outros membros chamados "backup" e "report" para ingressar no espelho (evite validação de manual no gerenciamento do portal).
mestre() {
rm -rf $BACKUP_FOLDER/IRIS.DAT
envsubst < ${MASTER_CONFIG} > ${MASTER_CONFIG}.resolved
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS
Artigo
Alessandro Marin · Nov. 26, 2020
Estou planejando implementar a Inteligência de Negócio (BI) com base nos dados de minhas instâncias. Qual é a melhor maneira de configurar meus bancos de dados e ambiente para usar o DeepSee?

Este tutorial aborda essa questão mostrando três exemplos de arquitetura para DeepSee. Começaremos com um modelo de arquitetura básico e destacaremos suas limitações. O modelo subsequente é recomendado para aplicações de Inteligência de Negócio (BI) de complexidade intermediária e deve ser suficiente para a maioria dos casos de uso. Terminaremos este tutorial descrevendo como aumentar a flexibilidade da arquitetura para gerenciar implementações avançadas.
Cada exemplo neste tutorial apresenta novos bancos de dados e mapeamentos globais, junto com uma discussão sobre por que e quando eles devem ser configurados. Ao construir a arquitetura, os benefícios fornecidos pelos exemplos mais flexíveis serão destacados.
Antes de começar
Servidores primários e analíticos
Para tornar os dados altamente disponíveis, a InterSystems geralmente recomenda usar as soluções de espelhamento ou sombreamento e então basear a implementação DeepSee no servidor espelho/sombra. A máquina que hospeda a cópia original dos dados é chamada de servidor Primário, enquanto as máquinas que hospedam cópias dos dados e as aplicações de Inteligência de Negócio (BI) costumam ser chamados de servidores Analíticos (ou, às vezes, de Relatórios)
Ter servidores primários e analíticos é muito importante, o principal motivo é evitar problemas de desempenho em qualquer um dos servidores. Verifique a documentação sobre a Arquitetura Recomendada.
Dados e código da aplicação
Armazenar dados de origem e código no mesmo banco de dados geralmente funciona bem apenas para aplicações de pequena escala. Para aplicações mais extensas, é recomendado armazenar os dados de origem e código em dois bancos de dados dedicados, o que permite compartilhar o código com todos os namespaces onde o DeepSee é executado, mantendo os dados separados. O banco de dados de dados de origem deve ser espelhado no servidor de produção. Este banco de dados pode ser somente leitura ou leitura-gravação. É recomendável manter o registro do diário habilitado para este banco de dados.
As classes de origem e as aplicações personalizados devem ser armazenados em um banco de dados dedicado nos servidores de produção e analítico. Observe que esses dois bancos de dados para o código-fonte não precisam estar sincronizados ou mesmo rodar a mesma versão do Caché. Normalmente, o registro no diário não é necessário, desde que o backup do código seja feito regularmente em outro lugar.
Neste tutorial teremos a seguinte configuração. O namespace do APP no servidor analítico tem o APP-DATA e o APP-CODE como bancos de dados padrão. O banco de dados APP-DATA tem acesso aos dados (a classe da tabela de origem e seus fatos) no banco de dados de dados de origem no Primário. O banco de dados APP-CODE armazena o código Caché (arquivos .cls e .INT) e outros códigos personalizados. Essa separação de dados e código é uma arquitetura típica e permite ao usuário, por exemplo, implantar com eficiência o código DeepSee e a aplicação personalizada.
Executar DeepSee em diferentes namespaces
As implementações de Inteligência de Negócio (BI) usando DeepSee geralmente são executadas a partir de namespaces diferentes. Nesta postagem, mostraremos como configurar um único namespace de APP, mas o mesmo procedimento se aplica a todos os namespaces onde a aplicação de inteligência de negócio é executada.
Documentação
Recomenda-se familiarizar-se com a página de documentação Executando a Configuração Inicial. Esta página inclui a configuração de aplicações web, como colocar DeepSee globais em bancos de dados separados e uma lista de mapeamentos alternativos para DeepSee globais.
* * *
Na segunda parte desta série mostraremos com a implementação de um modelo básico de arquitetura Excelente artigo!
Ansioso pela série completa
Pergunta
Henrique Dias · Dez. 16, 2020
Fala pessoal, tudo bem?
Criei uma tabela com 100k registros. No SELECT, retorna tudo belezinha. No entanto, quando tento criar um cubo utilizando essa tabela como base, o cubo é compilado com sucesso. No entanto, quando faço o BUILD, ele gera apenas 1 fato.
Alguém já se deparou com alguma situação similar?
Alguns detalhes:
Class diashenrique.olist.data.order Extends %Persistent
{
Property customerID As diashenrique.olist.data.customer;
Property orderStatus As %String;
Property purchaseTimeStamp As %TimeStamp;
Property approvedTimeStamp As %TimeStamp;
Property deliveredCarrierDate As %TimeStamp;
Property deliveredCustomerDate As %TimeStamp;
Property estimatedDelivery As %TimeStamp;
Index OrderStatusIndex On orderStatus;
Index customerIndex On customerID;
Index purchaseIndex On purchaseTimeStamp;
Storage Default
{
<Data name="orderDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>customerID</Value>
</Value>
<Value name="3">
<Value>orderStatus</Value>
</Value>
<Value name="4">
<Value>purchaseTimeStamp</Value>
</Value>
<Value name="5">
<Value>approvedTimeStamp</Value>
</Value>
<Value name="6">
<Value>deliveredCarrierDate</Value>
</Value>
<Value name="7">
<Value>deliveredCustomerDate</Value>
</Value>
<Value name="8">
<Value>estimatedDelivery</Value>
</Value>
</Data>
<DataLocation>^orderD</DataLocation>
<DefaultData>orderDefaultData</DefaultData>
<IdLocation>^orderD</IdLocation>
<IndexLocation>^orderI</IndexLocation>
<StreamLocation>^orderS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}
IRISAPP>d ##class(%DeepSee.Utils).%PrintBuildErrors("OrderCube")
0 build error(s) for 'OrderCube'
IRISAPP>Do ##class(%DeepSee.Utils).%BuildCube("OrderCube")
Building cube [OrderCube]
Existing cube deleted.
Fact table built: 1 fact(s) (1 worker(s) used)
Fact indices built: 1 fact(s) (1 worker(s) used)
Complete
Elapsed time: 0.209013s
Source expression time: 0.000003s
Abraços,
Henrique Caso alguém se depare com esse mesmo cenário no futuro, a causa do problema estava no tipo de indice.
@Eduard.Lebedyuk respondeu na Community em inglês e deixarei o link para quem quiser maiores detalhes.
https://community.intersystems.com/post/fact-table-1-x-sql-select-100k#comment-140541 Segue a resposta em português do @Eduard.Lebedyuk :
Os cubos dependem muito de índices de bitmap para serem executados rapidamente.
Principalmente, cada fato na tabela de fatos deve ser acessível por meio do índice de bitmap.
No passado, os índices de bitmap funcionavam apenas com inteiros positivos, mas agora parece haver uma abordagem %BID - essencialmente uma chave substituta.
Eu acho que o InterSystems BI deveria gerar um erro ou gerar um %BID ou oferecer para gerar um %BID se a propriedade id da classe do fato não for um inteiro positivo.
Artigo
Heloisa Paiva · Mar. 10, 2023
Introdução
Esse artigo tem a intenção de ser um simples tutorial sobre como criar conexões ODBC e trabalhar com elas, já que eu achei o assunto um pouco confuso quando estava começando, mas tive pessoas incríveis que pegaram minha mão e me guiaram para conseguir, e eu acredito que todos merecem esse tipo de ajuda também.
Vou dividir cada pequena parte em seções, então sinta-se à vontade para pular para a que sentir necessidade, apesar de eu recomendar ler o texto na íntegra.
Vou usar os dados de exemplo criados num artigo anterior, Tutorial - forma mais rápida de criar uma base de dados de exemplo: Samples.PersistentData, com as propriedades Name e Age.
Criando a conexão
Abra a Fonte de Dados ODBC - procure por ODBC na barra de pesquisa do seu computador e vai encontrar facilmente.
Selecione a aba DNS de sistema
Clique em Adicionar
Selecione o driver apropriado - para esse exemplo vou utilizar InterSystems IRIS ODBC35
Escolha um nome para a conexão
Digite o servidor, porta e namespace que quer conectar (ex.: IP 12.0.0.1, porta 1972 e namespace SAMPLE)
Digite o Usuário e a Senha que vai usar para se conectar
Clique em "Testar conexão" para ver se tudo está funcionando corretamente - se não for bem sucedida, cheque novamente usuário, senha, servidor, porta e namespace, e também cheque se o IRIS está ligado (para esse exemplo), ou se precisa de uma VPN para esse conexão.
OBS.: Eu não sei dizer se esses passos são similares para Linux ou iOS, perdão!
Usando sua conexão numa Business Operation numa produção
Esse é apenas um exemplo de como pode colocar essa conexão em prática, mas é um muito utilizado.
Com um Business Operation com adaptador "EnsLib.SQL.OutboundAdapter" em uma produção, abra a aba de configurações e expanda a parte de Parâmetros Básicos.
Você verá um input de DSN como esse:
Expanda o input e ache a conexão que acabamos de criar. Se não estiver ali, garanta que a conexão foi criada na Fonte de Dados ODBC correta (32-bit ou 64-bit). Se não aparecer, simplesmente siga os passos novamente na outra opção e cheque o input de DSN outra vez.
Credenciais
O IRIS pode precisar de um usuário e senha para poder acessar essa conexão, então você deve fornecê-los.
Logo abaixo do input de DSN, você vai encontrar um input de Credenciais com uma lupa ao lado.
Clique na lupa e você vai se encontrar no menu de credenciais.
Na aba à sua direita, clique em "Novo", digite um ID que facilitará que você identifique a credencial, o usuário e a senha necessários e salve.
Muito bem! Agora que você tem as credenciais, pode voltar para a produção e selecioná-las pelo ID que escolheu.
PS.: um exemplo para você testar
Para esse simples tutorial, eu criei a seguinte classe num namespace diferente do que tem a tabela "Sample.PersistentData":
Class Sample.ODBC.Operation Extends Ens.BusinessOperation
{
Parameter ADAPTER = "EnsLib.SQL.OutboundAdapter";
Property Adapter As EnsLib.SQL.OutboundAdapter;
Parameter INVOCATION = "Queue";
Method LegalAge(Request As Sample.request, Response As Sample.response) As %Status
{
// instanciate the response
Do Request.NewResponse(.Response)
// Execute the query and select the first result
Do ..Adapter.ExecuteQuery(.result, "SELECT Name, Age from Sample.PersistentData where Age > 20")
Do result.%Next()
// just for visualizing, sets the first result in the response
Set Response.result = result.%Get("Name")_" "_result.%Get("Age")
Quit 1
}
XData MessageMap
{
<MapItems>
<MapItem MessageType="Sample.request">
<Method>LegalAge</Method>
</MapItem>
</MapItems>
}
}
Conclusão
Obrigada por ler e eu espero que tenha sido útil!
Sinta-se à vontade para me contatar sobre qualquer dúvida.
Artigo
Anton Umnikov · jan 11, 2021
IRIS External Table é um projeto de código aberto da comunidade InterSystems, que permite usar arquivos armazenados no sistema de arquivos local e armazenamento de objetos em nuvem, como o AWS S3, como tabelas SQL. 
Ele pode ser encontrado no GitHub , Open Exchange e está incluído no InterSystems Package Manager, ZPM.
Para instalar o External Table a partir do GitHub, use:
git clone https://github.com/antonum/IRIS-ExternalTable.git
iris session iris
USER>set sc = ##class(%SYSTEM.OBJ).LoadDir("/IRIS-ExternalTable/src", "ck",,1)
Para instalar usando o ZPM Package Manager:
USER>zpm "install external-table"
## Trabalhando com arquivos locais
Vamos criar um arquivo simples parecido com este:
a1,b1
a2,b2
Abra seu editor favorito e crie o arquivo ou apenas use uma linha de comando no linux/mac:
echo $'a1,b1\na2,b2' > /tmp/test.txt
No IRIS SQL, crie uma tabela para representar este arquivo:
create table test (col1 char(10),col2 char(10))
Converta a tabela para usar o armazenamento externo:
CALL EXT.ConvertToExternal(
'test',
'{
"adapter":"EXT.LocalFile",
"location":"/tmp/test.txt",
"delimiter": ","
}')
E finalmente, consulte a tabela:
select * from test
Se tudo funcionar conforme o esperado, você verá uma saída como esta:
col1 col2
a1 b1
a2 b2
Agora volte ao editor, altere o conteúdo do arquivo e execute novamente a consulta SQL. Uau!!! Você está lendo os novos valores de seu arquivo local no SQL.
col1 col2
a1 b1
a2 b99
## Lendo dados a partir do S3
Em você pode obter acesso a dados atualizados constantemente sobre o COVID, armazenados pela AWS no data lake público.
Vamos tentar acessar uma das fontes de dados neste data lake: `s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states`
Se você tiver a ferramenta de linha de comando AWS instalada, pode repetir as etapas abaixo. Caso contrário, vá direto para a parte SQL. Você não precisa de usar um AWS específico instalado em sua máquina para acompanhar a parte SQL.
$ aws s3 ls s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/
2020-12-04 17:19:10 510572 us-states.csv
$ aws s3 cp s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv .
download: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv to ./us-states.csv
$ head us-states.csv
date,state,fips,cases,deaths
2020-01-21,Washington,53,1,0
2020-01-22,Washington,53,1,0
2020-01-23,Washington,53,1,0
2020-01-24,Illinois,17,1,0
2020-01-24,Washington,53,1,0
2020-01-25,California,06,1,0
2020-01-25,Illinois,17,1,0
2020-01-25,Washington,53,1,0
2020-01-26,Arizona,04,1,0
Portanto, temos um arquivo com uma estrutura bastante simples. Com cinco campos delimitados.
Para expor esta pasta S3 como um External Table, primeiro, precisamos criar uma tabela "regular" com a estrutura desejada:
-- create external table
create table covid_by_state (
"date" DATE,
"state" VARCHAR(20),
fips INT,
cases INT,
deaths INT
)
Observe que alguns nomes de campo como “Date” são palavras reservadas no IRIS SQL e precisam ser colocados entre aspas duplas. Em seguida, precisamos converter esta tabela “regular” para a tabela “externa”, com base no bucket AWS S3 e tipo CSV.
-- convert table to external storage
call EXT.ConvertToExternal(
'covid_by_state',
'{
"adapter":"EXT.AWSS3",
"location":"s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/",
"type": "csv",
"delimiter": ",",
"skipHeaders": 1
}'
)
Se você observar com atenção, os argumentos dos procedimentos EXT.ExternalTable são o nome da tabela e a string JSON, contendo vários parâmetros, como localização para procurar por arquivos, adaptador, delimitador, etc. Além da AWS S3, o External Table oferece suporte ao armazenamento BLOB do Azure, Cloud Buckets e o sistema de arquivos local. O GitHub Repo contém referências para a sintaxe e as opções suportadas em todos os formatos.
E finalmente, consulte a tabela:
-- query the table
select top 10 * from covid_by_state order by "date" desc
[SQL]USER>>select top 10 * from covid_by_state order by "date" desc
2. select top 10 * from covid_by_state order by "date" desc
date state fips cases deaths
2020-12-06 Alabama 01 269877 3889
2020-12-06 Alaska 02 36847 136
2020-12-06 Arizona 04 364276 6950
2020-12-06 Arkansas 05 170924 2660
2020-12-06 California 06 1371940 19937
2020-12-06 Colorado 08 262460 3437
2020-12-06 Connecticut 09 127715 5146
2020-12-06 Delaware 10 39912 793
2020-12-06 District of Columbia 11 23136 697
2020-12-06 Florida 12 1058066 19176
Compreensivelmente, leva mais tempo para consultar dados da tabela remota, do que na tabela "IRIS nativa" ou com base global, porém, ela é completamente armazenada e atualizada em nuvem e está sendo puxada para o IRIS "nos bastidores".
Vamos explorar mais alguns recursos do External Table.
## %PATH e tabelas, com base em vários arquivos
Em nossa pasta de exemplo, o bucket contém apenas um arquivo. Mais frequentemente, ele teria vários arquivos com a mesma estrutura, onde nome do arquivo identifica o carimbo de data/hora ou deviceid de algum outro atributo que desejaremos usar em nossas consultas.
O campo %PATH é adicionado automaticamente a cada External Table e contém o caminho completo para o arquivo de onde a linha foi recuperada.
select top 5 %PATH,* from covid_by_state
%PATH date state fips cases deaths
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-21 Washington 53 1 0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-22 Washington 53 1 0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-23 Washington 53 1 0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-24 Illinois 17 1 0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-24 Washington 53 1 0
Você pode usar este campo %PATH em suas consultas SQL como em qualquer outro campo.
## Dados ETL para "Tabelas Regulares"
Se sua tarefa é carregar dados do S3 em uma tabela IRIS, você pode usar o External Table como uma ferramenta ETL. Apenas faça:
INSERT INTO internal_table SELECT * FROM external_table
No nosso caso, se quisermos copiar os dados sobre COVID do S3 para a tabela local:
--create local table
create table covid_by_state_local (
"date" DATE,
"state" VARCHAR(100),
fips INT,
cases INT,
deaths INT
)
--ETL from External to Local table
INSERT INTO covid_by_state_local SELECT TO_DATE("date",'YYYY-MM-DD'),state,fips,cases,deaths FROM covid_by_state
## JOIN entre IRIS - tabela nativa e externa. Consultas federadas
External Table é uma tabela SQL. Ele pode ser unido a outras tabelas, usado em subseleções e UNIONs. Você pode até combinar a tabela IRIS “Regular” e duas ou mais tabelas externas de fontes diferentes na mesma consulta SQL.
Tente criar uma tabela regular, como os nomes de estado correspondendo com códigos de estado, como por exemplo, Washington – WA. E junte-a com nossa tabela baseada em S3.
create table state_codes (name varchar(100), code char(2))
insert into state_codes values ('Washington','WA')
insert into state_codes values ('Illinois','IL')
select top 10 "date", state, code, cases from covid_by_state join state_codes on state=name
Altere 'join' para 'left join' para incluir linhas para as quais o código de estado não foi definido. Como você pode ver, o resultado é uma combinação de dados do S3 e sua tabela IRIS nativa.
## Acesso seguro aos dados
A AWS Covid Data Lake é público. Qualquer pessoa pode ler dados dele sem qualquer autenticação ou autorização. Na vida real, você desejará acessar seus dados de forma segura, evitando que estranhos espiem seus arquivos. Os detalhes completos da AWS Identity and Access Management (IAM) estão fora do escopo deste artigo. Mas o mínimo que você precisa saber é que você precisa de pelo menos a chave de acesso e a chave secreta da conta da AWS para acessar dados privados em sua conta.
AWS usa autenticação de chave/segredo de conta para assinar solicitações. https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys
Se você estiver executando o IRIS External Table na instância EC2, a maneira recomendada de lidar com a autenticação é usar as funções da instância EC2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html. O IRIS External Table será capaz de usar as permissões dessa função. Nenhuma configuração extra é necessária.
Em uma instância local/não EC2, você precisa especificar o AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY, especificando variáveis de ambiente ou instalando e configurando o cliente AWS CLI.
export AWS_ACCESS_KEY_ID=AKIAEXAMPLEKEY
export AWS_SECRET_ACCESS_KEY=111222333abcdefghigklmnopqrst
Certifique-se de que a variável de ambiente esteja visível em seu processo IRIS. Você pode verificá-lo executando:
USER>write $system.Util.GetEnviron("AWS_ACCESS_KEY_ID")
Ela deve retornar o valor da chave.
ou instale o AWS CLI, seguindo as instruções aqui https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html e execute:
aws configure
O External Table poderá, então, ler as credenciais dos arquivos de configuração do aws cli. Seu shell interativo e o processo IRIS podem estar sendo executados em contas diferentes. Certifique-se de executar `aws configure` na mesma conta do seu processo IRIS.
Artigo
Henrique Dias · Mar. 6, 2021
@José.Pereira e eu criamos um bom projeto e gostaríamos de falar um pouco deste projeto para vocês.
O que é IRIS RAD Studio?
IRIS RAD Studio é a nossa ideia de low-code para mostrar o que é possível, oferecendo mais facilidade e flexibilidade aos desenvolvedores.
Por que?
Porque não!? Aplicações low-code tem ganho grande destaque no mercado nos últimos anos e a imagem abaixo mostra o "Magic Quadrant" atual fornecido pela Gartner para plataformas empresariais de aplicação low-code. O que mostra o quão interessante é esse mercado.
RESTForms2
RESTForms2 é uma das peças-chave para nosso projeto. Como descrito por @Eduard.Lebedyuk neste artigo, RESTForms é um backend REST API para aplicações web modernas
https://community.intersystems.com/post/restforms-rest-api-your-classes
Funcionalidades
Com a utilização de RESTForms2, classes persistentes herdadas de dc.irisrad.FormAdaptor, ganham automaticamente um formulário CRUD (Create, Read, Update, Delete). Os formulários disponíveis são exibidos na página inicial.
Cada formulário, além das funcionalidades básicas de CRUD, também possui:
Pesquisa geral
Pesquisa avançada de um campo em específico
Criação de filtros combinados
Agrupamento
Exportação do Datagrid para Excel
Além da geração automática dos formulários baseado no JSON fornecido pelo RESTForms2, uma coisa que queremos oferecer aos usuários é o nosso Import Wizard.
Import Wizard
A funcionalidade apresentada anteriormente com o projeto iris-analytics-package, permite a qualquer usuário:
Importar um arquivo CSV
Criar uma classe persistente de acordo com o arquivo importado
Criar um cubo para ser utilizado pelo InterSystems Analytics
Gerar um dashboard de exemplo com base nos dados
E se além disso, você também pudesse editar esse arquivo? Se você tivesse a possibilidade de criar novas informações, editar as informações providas pelo CSV ou até mesmo deletar linhas desnecessárias?
Com IRIS RAD Studio você pode! 😃
Criando novas classes
Se você possui classes existentes que herdam dc.irisrad.FormAdaptor, já pode tirar proveito dos recursos oferecidos pelo RAD Studio.
Para novas classes, criamos um endpoint onde você fornece a classe desejada no formato JSON e voilà
Exemplo:
{ "name": "My.ClassName", "displayFormName": "My tasks", "displayProperty": "text", "fields": [{ "name":"text", "displayName":"Task name", "type":"%Library.String", "required": false },{ "name":"taskDate", "displayName":"Task date", "type":"%Library.TimeStamp" },{ "name":"important", "displayName":"Important", "type":"%Library.Boolean" },{ "name":"completed", "displayName":"Completed", "type":"%Library.Boolean" }]}
Facinho, né?!
Hmmm... mas você deve estar se perguntando "E se eu não tenho essa familiaridade toda com JSON, ou se não entendo nada da parte técnica?"
Calma... IRIS RAD Studio também oferece uma interface gráfica para a criação de novas classes!
Criando novas classes - Wizard
A ideia do low-code é justamente evitar que código seja escrito pelo seu usuário. E isso inclui descrever uma classe e suas propriedade no formato JSON!
A interface criada permite que você crie o nome da sua classe, forneça a descrição que o seu formulário terá.
Clicando no botão Save, ela abrirá um datagrid onde você pode definir as propriedades da sua classe, ou se preferir chamar assim, as colunas da sua tabela.
Assim que terminar de definir, basta clicar em Compile e está pronto para utilizar seu novo formulário!
Roadmap
Acreditamos que esse projeto tem muito potencial para ser explorado. Caso bem recebido pela comunidade, gostaria de explorar essas possibilidades e criar um roadmap com todos.
Assim, cada vez mais teremos um produto que atenda a necessidade geral.
Demo
Disponibilizamos a aplicação no link abaixo:https://irisrad.contest.community.intersystems.com/csp/irisapp/login.html
Se você quer saber o usuário e senha, primeiro, precisa votar no nosso projeto! Sacanagem hehehe nós informamos o usuário/senha na nossa página no OpenExchange
https://openexchange.intersystems.com/package/iris-rad-studio
Hora da votação
Se você gostou da nossa ideia, e acredita que merecemos seu voto, corre lá e nos ajude 😃 Seu voto é muito importante para nós
iris-rad-studio
https://openexchange.intersystems.com/contest/12
Artigo
ROBSON SERPA DA ROSA · Jun. 28, 2021
Uma VIEW em SQL é basicamente uma instrução SQL preparada.Deve ser executado e montado como qualquer outra consulta SQL.VIEW MATERIALIZADA significa que o conteúdo é coletado antes das mãos e pode ser recuperado com bastante rapidez.Eu vi o conceito primeiro com meu concorrente favorito chamado O * e eles fizeram muito barulho sobre isso. { favorite: because I could win every benchmark against them }
Dependendo da construção individual dessas views materializadas, atualizações e manutenção podem ser necessárias.No Caché / IRIS isso existe quase desde sempre e consideramos isso normal e um fato dado.A maioria dos desenvolvedores simplesmente não está ciente disso e, com um pouco de polimento, pode ser apresentado como um excelente recurso.Além disso, qualquer atualização e manutenção acontecem como uma funcionalidade integrada sem nenhum esforço extra.
Veja este exemplo:Em nossa famosa classe Sample.Person no namespace SAMPLES, estendi e reconstruí seu índice.
/// Define an index for <property>Name</property>.Index NameIDX On Name [ Data = (Name, Home.State, SSN) ];
E sendo experiente e confortável com o gerador de consultas, você sabe que
SELECT ID, Name, Home_State, SSN from Sample.Person
será executado rapidamente usando apenas o índice global ^ Sample.PersonI ("NameIDX")e nunca toque em seus dados globais.Essa é basicamente a funcionalidade de uma View materializada e a atualização está implícita.
Definido como VIEW (de MgmtPortal, já que Studio não é tão prático) você obtém esta classe.
Class Sample.Person.NameView [ ClassType = view , CompileAfter = Sample.Person , DdlAllowed , Not ProcedureBlock , SqlTableName = NameView , ViewQuery = { SELECT ID , Name , Home_State , SSN from Sample.Person } ] { Parameter READONLY = 1; }
Mas se você quiser um pouco mais de conforto, como um backlink para seus dados, pode mapear o próprio índice global.Portanto, você pode aplicar a sintaxe JOIN implícita e ter uma tabela totalmente funcional como aqui
SELECT Name, BaseClass->DOB, HomeState, SSN,"%CLASSNAME",BaseClass FROM Sample_Person.NameIDX
e aqui está a definição da classe e você deve projetá-la manualmente
/// mapped index /// Index NameIDX On Name [ Data = (Name, Home.State, SSN) ];Class Sample.Person.NameIDX Extends %Persistent [ Final ]{Property IndexName As %String [ InitialExpression = "NameIDX", ReadOnly ];Property SQLUPPERname As %String [ ReadOnly ];Property BaseClass As Sample.Person [ ReadOnly ];Index min On (IndexName, SQLUPPERname, BaseClass) [ IdKey ];/// Classname of Index SourceProperty %CLASSNAME As %String [ ReadOnly ];/// Person's name.Property Name As %String [ ReadOnly ];/// Person's home address. This uses an embedded object.Property HomeState As %String [ ReadOnly ];/// Person's Social Security number. This is validated using pattern match.Property SSN As %String(PATTERN = "3N1""-""2N1""-""4N") [ ReadOnly ];Parameter READONLY = 1;Parameter MANAGEDEXTENT As INTEGER = 0;Storage Default{ <Data name="NameIDXDefaultData"> <Value name="1"> <Value>%CLASSNAME</Value> </Value> <Value name="2"> <Value>Name</Value> </Value> <Value name="3"> <Value>HomeState</Value> </Value> <Value name="4"> <Value>SSN</Value> </Value> </Data> <DataLocation>^Sample.PersonI</DataLocation> <DefaultData>NameIDXDefaultData</DefaultData> <IdLocation>^Sample.Person.NameIDXD</IdLocation> <IndexLocation>^Sample.Person.NameIDXI</IndexLocation> <StreamLocation>^Sample.Person.NameIDXS</StreamLocation> <Type>%Library.CacheStorage</Type>} }
Este é um exemplo de codificação funcionando no Caché 2018.1.3 e IRIS 2020.2
Não será sincronizado com as novas versões
Também NÃO é atendido pelo Suporte da InterSystems!
Artigo
Danusa Calixto · Out. 27, 2022
Por que eu amo ObjectScript e por que eu acho que poderia amar Python ainda mais
Eu estava olhando o tópico de mensagens sobre o assunto "Desempenho ao construir uma string separada por vírgulas" e comecei a escrever uma resposta. No entanto, me distraí, a página foi atualizada e perdi meu texto. Não podia gastar tempo reescrevendo minha resposta, então comecei a escrever este documento em vez disso.
Comecei a escrever na linguagem MUMPS no início da minha carreira. Eu escrevia blocos de código bastante concisos e densos em que exercícios como o exemplo da string eram verdadeiros desafios. O desempenho dos servidores VAX ou Digital DEC eram aproveitados até a última gota. Planejávamos onde globais importantes ficariam em um disco. Quando o Caché foi lançado, ainda estávamos trabalhando com M/SQL. Durante um período, estive envolvido em diversas comparações de desempenho entre o Caché e o Oracle, Sybase e SQL Server. Criávamos um esquema de algumas tabelas, preenchidas com milhões de registros, e executávamos várias pesquisas no banco de dados resultante. Eu costumava escrever duas versões de declaração SQL. Uma era uma declaração SQL pura, e a outra era uma consulta personalizada que eu escrevia na definição de classe. A maior parte da lógica está no "Fetch", e eu elaborava esse método para maximizar os indíces definidos e usar ^CachéTemp para qualquer junção complexa de resultados provisórios. Às vezes, eu criava jobs de uma ou mais subconsultas que gerariam os globais temporários e resolvia as junções após a conclusão de todos os processos de jobs. O resultado poderia ser resumido da seguinte maneira:
Inserir dados no banco de dados usando SQL ou Caché Objects sempre foi mais rápido do que qualquer outro DB. Usar COS puro e conjuntos globais diretos era uma ordem de grandeza mais rápida do que SQL, Objects e qualquer outro banco de dados. O banco de dados resultante teria aproximadamente metade do tamanho de um criado por qualquer banco de dados relacional.
Ao comparar o código que escrevi no meu método "Fetch" com o código gerado pelo Caché SQL Engine, vi que usei menos variáveis, 25% menos linhas e o código ficou mais legível.
O número de leituras de blocos de dados físicos seria praticamente o mesmo que o código gerado pelo M/SQL. No entanto, o número de leituras lógicas do pool de buffers globais seria 20% menor que o M/SQL.
Recorri a todos os truques possíveis de desenvolvedores MUMPS. Usei comandos como "execute", "job" (criando threads para lidar com subconsultas em paralelo de forma eficaz), indireção e pós-condições. Recomendamos aos desenvolvedores que não usem esses recursos de linguagem para escrever código legível e capaz de ser mantido por outros desenvolvedores.
Eu inicializaria variáveis desta forma:
set (a,b,c,d)="",(x,y,z)=0,p1=+$h,p2=...,pN=99
Espremi o máximo de expressões possível em uma linha de código. Acreditávamos que a leitura de cada linha de código no "buffer de execução" resultava em um custo. Portanto, o número de linhas de código executadas sempre teve um efeito direto e inverso no desempenho.
Quando trabalho com código escrito por outro desenvolvedor e noto blocos de código que consistem em um comando definido por cada linha, fico um pouco agitado e sempre condenso essas 30 linhas em uma. Me apaixonei por Caché Objects. Vinte e cinco anos depois, esse caso durou mais que dois relacionamentos sérios e um casamento. Definições de classe, com nomes de propriedade precisos e bastante legíveis, indexação de bitmap em tudo, a menos que a indexação de busca possa fazer melhor. Quando possível, relacionamentos pais-filhos em vez de um-muitos. Uso uma chave primária personalizada em tabelas de código quando a indexação de bitmap não é necessária porque set record=$g(^global(code)) sempre será mais rápido que
set record="",rowId=$o(^IndexGlobal("IndexName",code,"")) set:$l(rowId) record=^Global(rowId)
Havia algumas formas de declarações SQL select com que o M/SQL não era compatível ou executava mal. Em geral, o Caché era 2 a 3 vezes mais rápido do que qualquer outro banco de dados.
Ao longo dos anos, o mecanismo SQL melhorou significativamente. A indexação de bitmap e iFind foi lançada. Usamos a indexação iFind nos nomes e endereços de pacientes em um banco de dados de 15 milhões de pessoas. Todos os outros campos são indexados por bitmap. Quando recebemos uma Pesquisa de Paciente de FHIR com vários parâmetros, oferecemos suporte a todos os qualificadores e operadores de especificação FHIR. Construímos uma declaração SQL que começa com uma junção em todas as entidades do paciente de FHIR, que armazenamos em classes persistentes. Estou reivindicando o uso do repositório IRIS for Health para nossa próxima fase de desenvolvimento. O IRIS teve dois lançamentos e amadureceu desde a primeira vez que trabalhei com ele na versão 2019.1. A junção é seguida por qualquer cláusula iFind em Nomes e Endereços, se especificada nos critérios de pesquisa. As cláusulas AND/OR são adicionadas para os campos nos critérios de pesquisa que sabemos serem compatíveis com índices bitmap. As pesquisas determinísticas ou probabilísticas que realizamos são tão rápidas e precisas que ainda me fazem pular de animação (na minha idade!!!).
Preciso confessar que nunca gostei do SQL quando fazia parte de um grupo cada vez menor de desenvolvedores que escreviam código MUMPS no final dos anos 80. Meus colegas foram rápidos em aderir ao Oracle ou SQL Server. Às vezes, era difícil não entrar em um estado de desespero quando ouvia opositores gritando: "O MUMPS está morto".
Então, na conferência anual do MUMPS em Dublin, acordamos em determinada manhã com um bilhete enfiado debaixo das nossas portas anunciando que a InterSystems havia comprado a DTM. Em uma conferência realizada um ano depois em Birmingham, eu estava trabalhando para a InterSystems e estávamos apresentando formulários do Visual Basic usando o Caché de dll que adquirimos quando compramos Data Tree. A Micronetics estava no estande em frente ao nosso, e eles não tinham um dll. O sistema de som deles era mais alto, mas nós havíamos vencido. Levaria mais um ano para comprarmos DSM da Digital e, por fim, MSM da Micronetics. Não havia mais como recuar. Lembro de mostrar o M/SQL a um cliente em Birmingham que escrevia um software de contabilidade. Um dos seus clientes era o Barings Bank, que havia acabado de perder 859.000.000 GBP devido ao trader desonesto Nick Leeson. Não pude deixar de configurar meu banco de dados de exemplo para poder executar uma consulta SQL que provavelmente não era mais complexa que "SELECT sum(Total) from Accounts WHERE .... and AccountNumber="666..". A conta tinha o mesmo número daquela usada por Nick Leeson para esconder as negociações que estava fazendo para recuperar sua situação, que piorava a cada toque do sino do pregão no mercado de ações de Singapura. Lembro de ficar rindo silenciosamente, em parte pela referência implícita ao colapso do Barings Bank, mas também porque a consulta foi realmente executada, forneceu a resposta correta e não levou mais de um minuto (nenhuma dessas coisas era uma certeza na época).
Essa é a única memória que tenho de gostar do SQL. Eu lidava com vários públicos de DBA do Oracle e SQL Server e demonstrava o Caché, Caché e VB, Caché Objects e Caché SQL. O Caché Objects me encantava: tão elegante, óbvio, maleável e legível. A sintaxe de objetos (em qualquer linguagem) é muito mais natural para mim do que qualquer declaração SQL. Quando tivemos a oportunidade de pegar o esquema do aplicativo de um cliente em potencial, executá-lo através do importador SQL e traduzir o conjunto de procedimentos armazenados que o cliente em potencial incluiria no esquema em Caché Objects ou Caché Globals puro, me familiarizei muito com a leitura do plano de execução SQL e a consulta armazenada gerada. Entrei em longas conversas com Ariel Klausner sobre a consulta SQL que o cliente em potencial me deu e não estava funcionando, e essa seria a diferença entre: ver os DBAs do Oracle saindo da sala de reuniões de volta para a segurança do ajuste do índice e as 6 horas garantidas de inatividade dos sistemas todos os dias durante os backups, onde eles poderiam trazer seus aplicativos relacionais de volta à vida em prontidão para os próximos dias de negociações, ou a emoção de conquistar um cliente que havíamos buscado por meses e estava mais interessado na velocidade do Caché, Orientação de Objetos, gateways .Net ou Java e a elegância simples do CSP de corretagem. Acredito que "por que escrever aplicativos DENTRO de um ambiente de DB?" não é uma pergunta. Primeiro, crio um banco de dados para meu código e outro para meus globais e, ali mesmo, tenho um ponto de separação. Todos nós crescemos nos últimos 25+ anos pensando em Classes, Objetos, ObjectScript e Globais como todos agrupados. Argumento que, no tempo de execução, o código sendo executado no buffer é OBJ. Basicamente, o código OBJ é uma mistura de código C compilado, código de máquina puro otimizado para a plataforma em que está sendo executado e alguns resquícios da definição de classe necessária se você estiver usando $classname, $classmethod, $property e outros fatores. Grande parte do "mecanismo" do Caché ou IRIS é escrito em ObjectScript, provando que essa é uma linguagem perfeita para trabalho. É uma linguagem que pode ser explícita, abreviada e muito compacta. Ela contém todos os operadores e as construções de qualquer linguagem moderna (if, ifelse, else, try - catch, while, for [para ser justo, nossa implementação de FOR é maravilhosa: | for i="apples","pears","Nigel","Fruit" {} | for {} | for i=$$$StartGValue():$$$Increment():$$$EndValue() | ]). Se um dos primeiros criadores do MUMPS tivesse chamado $order de "$next", ele seria imediatamente reconhecível como Next(), conforme encontrado em todas as outras linguagens que iteram por uma array. $PIECE é um pouco peculiar, mas só porque todos os outros bancos de dados usam campos de tamanho fixo. O conceito das strings delimitadas usadas como construção do banco de dados é estranho para um DBA do SQL. Ao analisar o código de máquina compilado de qualquer uma das formas de banco de dados, as instruções se movem pela string, caractere por caractere, e contando o número de caracteres ou fazendo isso enquanto procuram por um delimitador de campo específico.
$list conseguiu um desempenho um pouco melhor do que $piece, mas à custa de um ou dois bytes adicionais no início de cada campo. No entanto, ainda consumiu menos espaço do que os campos de comprimento fixo. O motivo pelo qual todo código de sistema é escrito em ObjectScript até hoje é porque a linguagem é muito eficiente e legível. Além disso, quando os principais desenvolvedores do Caché/IRIS, Scott Jones, Dave McCalldon e Mo Chung, precisavam de algo onde o ObjectScript fosse inadequado, eles escreviam em C e enterravam no Kernal.
Segundo, se eu tiver uma definição de tabela e campos que exigem alguma forma de formatação ou validação em cima e além das restrições óbvias de tipo e comprimento, então quero escrever esse código e mantê-lo bastante próximo à própria definição do campo. Por que eu entraria em outra linguagem, outro ambiente, para escrever essa validação? Os bancos de dados relacionais usam procedimentos e gatilhos armazenados para lidar com essa validação usando a linguagem SQL para expressar a lógica de validação. Se encontrar um programador que prefira usar SQL para escrever lógica complexa em vez de Basic, C#, C++, ObjectScript ou Python, eu compro uma cerveja para você na próxima vez que eu passar por Viena :-)
Na universidade, aprendi a programar em Fortran e Pascal. Pascal era uma linguagem utilizável e perfeitamente legível. Como um jovem de 19 anos que ficava facilmente empolgado, o fato de que o compilador Pascal pudesse ser escrito nessa linguagem me fascinou. Mais tarde, aprendi COBOL. WTF??? No entanto, tenho um amigo que é desenvolvedor da Sage Accounting e escreve nessa linguagem porque a Sage Accounting foi escrita em COBOL. Páginas e páginas da linguagem mais detalhada, ilegível e inutilizável que já vi. Na verdade, o COBOL ainda é muito usado.
Você poderia pensar que Pascal ultrapassaria facilmente COBAL e até Basic. Mas isso não aconteceu. Por quê? É simples. Essa linguagem não era usada nos aplicativos bancários (o COBOL foi amplamente adotado em grandes aplicativos de processamento em lotes de mainframe, como Accounting). Brincávamos dizendo que os bancos não queriam comprar o modelo Caché porque não era sofisticado o suficiente. Não era porque o ObjectScript não conseguisse fazer o processamento transacional desses aplicativos bancários. Nós éramos comprovadamente mais rápidos do que qualquer tecnologia que eles estivessem usando. O problema era que eles haviam gastado muito dinheiro nos sistemas que tinham e no hardware necessário para executar esses Lotes durante a noite para os bancos abrirem às 9h do dia seguinte. As salas caras dos servidores com gás radônio e sistemas de filtragem removem até mesmo as menores partículas de poeira para que não caiam em um disco ou uma fita magnética, causando a perda de um dia inteiro de transações de contas.
Pascal deveria ter vivido mais do que o Basic e talvez isso tivesse acontecido se a Microsoft não houvesse criado o Visual Basic e entrado em competição com Delphi e Borland. O IDE parecia mesmo com o VB, mas usava Pascal em vez de Basic. Tudo isso estava acontecendo enquanto a Microsoft lançava o C#, porque eles tinham que acomodar todos os programadores de C++ e certamente não conseguiriam conquistá-los com o Basic. Eles também estavam ameaçando lançar a própria versão do Java ou remover a compatibilidade com ele, porque o Java rodar em plataformas de hardware em que o Windows nunca seria capaz de rodar era algo que os incomodava. A Microsoft só recuou quando os avanços da tecnologia tornaram o conceito de máquinas virtuais ou containers uma opção de implantação realista. Então, Pascal e Delphi simplesmente desapareceram. Fiz uma pesquisa rápida no Google e há um interpretador de Pascal para Android, então ele ainda existe.
Considerando que Pascal era uma linguagem, ao contrário do Basic, que era apenas uma linguagem de certa forma. No entanto, ele foi usado pela Microsoft para os scripts de aplicativos como o Excel e uma conexão de propriedade com o SQL Server. Isso permitiu vincular dois ambientes que eram intrinsecamente inadequados sem o incômodo de atender aos padrões ODBC e JDBC. Os padrões eram fortemente apoiados pelo Oracle, Sybase e praticamente todos que precisavam fornecer um gateway para as versões proprietárias do SQL. Assim, o Basic sobreviveu. Estou feliz por ter começado minha carreira de programador com o Pascal. Depois, tive uma grande surpresa ao escrever programas COBOL por um ano trabalhando para uma empresa de seguros. Cheguei às costas úmidas e cinzentas do Reino Unido e comecei meu primeiro emprego, que, por acaso, usava MUMPS. Toda a evolução do MUMPS para CachéObjectScript, Objects, Object Gateways, .Net e Java, Caché Basic e MultiValueBasic e agora Python. O Python fecha um ciclo e, em certo sentido, prova que o ObjectScript não é uma aberração em uma tecnologia de banco de dados não relacional rejeitada.
Caché Globals são arrays esparsas multidimensionais tão convenientes para a própria natureza dos dados de saúde que não importa o esforço do Oracle e da Microsoft em consumir esse espaço no mercado. Ainda que tenham matado Pascal e Fortran, e até Basic, eles não conseguiram matar a InterSystems. Lembro de participar de um seminário do Oracle sobre "Oracle para a Saúde". A apresentadora falava sobre o Oracle na área da saúde que, ela nos garantiu, dominaria o mercado da saúde de uma vez por todas. Levantei minha mão e perguntei: "Não é isso o que vocês afirmam em todos os grandes lançamentos há anos? Vocês fracassaram antes. Por que fariam melhor desta vez?" Ela olhou para mim e perguntou, "Quem é você?". Respondi: "Sou da InterSystems. Dominamos o mercado da saúde há 35 anos. Conseguimos isso porque nossa tecnologia nasceu no Massachusetts General Hospital. E adivinhe. Eles ainda executam os sistemas centrais nas nossas tecnologias". Nessa hora, dois seguranças corpulentos me tiraram do auditório.
Então, o Oracle com o pSQL tem o Java. A Microsoft tem o SQL Server, C# e tSQL e, quando você precisa interagir com o Java, fica sujeito ao JDBC. Da mesma forma, com Java, se você precisa conversar com tabelas do SQL Server, é obrigado a usar ODBC. Onde ficamos? Bem, chegamos à grande ideia de ter wrappers para .Net e Java. Ao usar o ObjectScript, eu instancio uma instância da Classe A. Na verdade, não importa se a Classe A é uma classe .Net, Java ou ObjectScript, porque eu instancio esses objetos usando exatamente a mesma sintaxe em todos os casos. Depois, invoco os métodos de classe ou instância para manipular os objetos. O interior desses métodos não é importante, porque a sintaxe para interagir com essas classes e os métodos é basicamente idêntica, independentemente do que elas contenham.
Junto vem o Python, que compartilha vários recursos com o ObjectScript, pois é uma linguagem interpretada, e não compilada. É bastante legível e utilizável. Assim como o Caché ObjectScript encontrou um nicho nos dados não estruturados, o Python encontrou um nicho no mundo da modelagem matemática, ML, IA e muito, muito mais. Esse não é um mundo em que C# ou Java se sintam particularmente confortáveis. Aliás, nem o ObjectScript. Assim, a InterSystems foca em fornecer funcionalidades cada vez mais poderosas para manipular grandes quantidades de dados não estruturados, além de lançar iFind e iKnow, algumas técnicas de indexação muito inteligentes e algoritmos de correspondência de probabilidade. Então, você convida o Python para se aconchegar nas nossas arrays esparsas multidimensionais, trazendo milhões de bebês .py que fazem praticamente tudo o que você precisa. É a combinação perfeita. Por precaução, esqueci de mencionar que várias arquiteturas que dominam o mundo do desenvolvimento de páginas da Web são completamente baseadas em JS (Angular.js, REACT.js, Vue.js, Bootstrap — certo, não há JS, mas ele está em tudo menos no nome — e Node.js) e Arrays de JS. O JS não vai desaparecer tão cedo. No entanto, será interessante ver o que vai acontecer com a Golang, se é que você me entende. Vi entradas baseadas em arrays de JS nas últimas competições de código. Se existe uma tecnologia que entende arrays melhor do que qualquer outra, é a IRIS.
Penso naqueles dias, sentado no meu escritório na empresa onde trabalhava no coração de Londres. Em determinado momento, a empresa estava cheia de programadores MUMPS, mas ela os transformou em programadores SQL relacionais, que depois se tornaram redundantes. Lembro da sensação de começar a questionar se minha fé no MUMPS ser simplesmente a melhor linguagem que já havia encontrado poderia estar errada. A linguagem e as empresas que construíram interpretações dessa linguagem morreriam. Isso me deixou muito triste porque, até então, havia aprendido cinco outras linguagens de programação (APL, Basic, Fortran, COBOL e Pascal) antes de descobrir o MUMPS, e o MUMPS era tão simples. Fácil de escrever, ler e implantar. Em outras palavras, era tão natural para mim quanto o inglês, e tinha um ritmo parecido com os hinos que cantávamos na escola metodista que frequentei:
Onward, Christian soldiers!
Marching as to war,
With the cross of Jesus
Going on before.
Christ, the royal Master,
Leads against the foe;
Forward into battle,
See his banners go!
Mas ele não morreu. A música mudou um pouco:
A bordo, Nigel Saaalllm
Voando para a guerra
Com seu cartão de crédito internacional
Indo à frente.
John, o Mestre, McCormick
Lidera contra o inimigo (Microsoft)
Avançando na batalha
Veja seus duty frees partindo
CacheObjectScript era ainda melhor que o MUMPS se isso era possível. CacheObjects parecia tão legal quando demonstrado para uma audiência pelas primeiras vezes, e CacheSQL deixou seus dias de M/SQL para trás e melhorou muito ao longo dos anos. Ainda assim, eu particularmente não gosto de escrever muito em SQL, mas encontrei um bom equilíbrio entre Objects, SQL e referências globais diretas conforme relaxei. Meu código era bastante orientado para as referências globais diretas, com um pouco de OO e o mínimo de SQL. Quando vi com os produtos que o código gerado era compacto, elegante, eficiente e legível, o equilíbrio mudou novamente. Agora, uso nomes globais diretos raramente, muito Objects e uma quantidade razoável de SQL.
Para trabalhar com Python, minha mente precisará ver padrões diferentes do meu código ObjectScript. Há muitos "__abc__" e outras estruturas estranhas. No entanto, depois de escrever algumas páginas de código py e me afastar, como faço ao pintar a óleo, os padrões saltarão. Assim como vejo a música como sinestesia de cores, meus programas py codificados por cores fluindo pela página também começarão a parecer aquarela ou até mesmo uma pintura a óleo pesada. Ficarei encantado, e estará tudo bem. Danusa, parabéns pelo artigo faço um pouco de suas palavras na longa estrada de programação escrevendo em diversas linguagens (Cobol, Dataflex, Clipper, VB, C#, Pascal...), em meu primeiro contato com o MUMPS me encontrei e foi um pouco complicado trabalhavamos com dois Sistemas Operacionais onde pela manhã rodava programas Cobol e na tarde MUMPS no mesmo Servidor e foi assim que me apaixonei pelo código MUMPS pela simplicidade, facilidade de manutenção e muito leve, ao conhecer o CACHÉ 2.0 e programando em Pascal percebi o legado que poderia vir de bom ao passar dos anos, hoje trabalho com IRIS e sempre eu lia os artigos e comentários CACHÉ somente para Saúde, muito particularmente eu acho que não é bem assim, consegui escrever códigos em transações financeiras no qual colegas de profissões me diziam voce é um louco não vai conseguir com esse CACHÉ, finalizando esses meus códigos hoje faço uma autorização para cartões em 3 segundos usando IRIS e quanto aos concorrentes de 8 a 13 segundos, essa mágica esta no COS.
Artigo
Heloisa Paiva · Abr. 20
Sei que pessoas completamente novas no VS Code, Git, Docker, FHIR e outras ferramentas podem, às vezes, ter dificuldades com a configuração do ambiente. Por isso, decidi escrever um artigo que percorre todo o processo de configuração passo a passo para facilitar o início.
Eu realmente agradeceria se você pudesse deixar um comentário no final – me diga se as instruções foram claras, se algo ficou faltando ou se há mais alguma coisa que você acharia útil.
A configuração inclui:
✅ VS Code – Editor de código✅ Git – Sistema de controle de versão✅ Docker – Executa uma instância do IRIS for Health Community✅ Extensão REST Client do VS Code – Para executar consultas à API FHIR✅ Python – Para escrever scripts baseados em FHIR✅ Jupyter Notebooks –Para tarefas de IA e FHIR
Antes de começar: Certifique-se de ter privilégios de administrador no seu sistema.
Além de ler o guia, você também pode seguir os passos nos vídeos:
Para Windows
Para macOS
Há uma enquete no final do artigo, por favor, compartilhe seu progresso. Seu feedback é muito apreciado.
Então, vamos começar!
1. Instalar o Visual Studio Code (VS Code)
O VS Code será o editor principal para o desenvolvimento.
Windows & macOS
Acesse a página de download do VS Code: https://code.visualstudio.com/
Baixe o instalador para o seu sistema operacional:
Windows: arquivo .exe
macOS: arquivo .dmg
Execute o instalador e siga as instruções.
(Apenas Windows): Durante a instalação, marque a caixa para "Adicionar ao PATH".
Verificar a instalação:
Abra um terminal (Prompt de Comando, PowerShell ou Terminal do macOS)
Execute:
code --version
Você deverá ver o número da versão.
2. Instalar o Git
O Git é necessário para controle de versão, clonagem e gerenciamento de repositórios de código.
Windows
Baixe a versão mais recente em: https://git-scm.com/downloads
Execute o instalador:
Escolha "Use Git from the Windows Command Prompt".
Mantenha as configurações padrão e finalize a instalação.
Verifique a instalação:
git --version
macOS
Abra o terminal e execute:
git --version
Se o Git não estiver instalado, o macOS solicitará que você instale as Ferramentas de Linha de Comando. Siga as instruções.
3. Instalar o Docker
O Docker é necessário para executar o InterSystems IRIS for Health Community.
Windows
1. Baixe o Docker Desktop em: https://www.docker.com/products/docker-desktop2. Execute o instalador e siga a configuração.3. Reinicie seu computador após a instalação.4. Habilite o Backend WSL 2 (se solicitado).5. Verifique a instalação.
Observação importante: A instalação do Docker requer privilégios de administrador na sua máquina e pelo menos uma reinicialização.
macOS
1. Baixe o Docker Desktop para Mac em: https://www.docker.com/products/docker-desktop2. Instale-o arrastando o aplicativo Docker para a pasta Aplicativos.3. Abra o Docker no menu Aplicativos.
Para garantir que o motor do Docker Desktop esteja rodando no Windows ou macOS, siga estes passos:
Inicie o Docker Desktop
Windows: Abra o Docker Desktop no menu Iniciar. O ícone da baleia do Docker deve aparecer na sua bandeja do sistema.
Mac: Inicie o Docker Desktop na pasta Aplicativos. Você verá o ícone da baleia do Docker na barra de menus assim que estiver em execução.
Aguarde a Inicialização
Assim que você iniciar o Docker Desktop, o motor pode levar alguns instantes para começar. Procure por uma mensagem de status indicando que o Docker está "em execução" ou "iniciado".
Verifique via Terminal/Prompt de Comando::
Abra um terminal (ou Prompt de Comando/PowerShell no Windows) e execute:
docker --version
ou
docker info
Solução de problemas
Se o motor não estiver rodando, tente reiniciar o Docker Desktop ou verifique se há mensagens de erro na interface do Docker Desktop. Além disso, certifique-se de que seu sistema atende aos requisitos do Docker Desktop. Você pode ver mensagens de erro confusas que referenciam pipes se tentar construir uma imagem Docker sem o Docker Desktop em execução.
4. Construindo a imagem do IRIS for Health e executando-a usando o Docker
Antes de podermos iniciar um contêiner Docker executando o IRIS for Health Community (que inclui nosso servidor FHIR), devemos construí-lo.
Clone o repositório FHIR para um diretório conveniente no seu sistema de arquivos. Abra um terminal no VS Code e clone este repositório com o seguinte comando:
git clone https://github.com/pjamiesointersystems/Dockerfhir.git
Navegue até esse diretório e abra a pasta no VS Code. Siga as instruções no arquivo readme para construir e executar o contêiner. Uma etapa crucial é garantir que o repositório base esteja disponível no seu Docker. Você pode fazer isso através do comando no terminal do VS Code:
docker pull containers.intersystems.com/intersystems/irishealth-community:latest-em
Você deverá ver uma confirmação após alguns minutos.
Navegue até o diretório no VS Code onde você vê o arquivo docker-compose.yaml e então execute o comando:
docker-compose build
Isso iniciará o processo de construção, que pode levar até 10 minutos, durante os quais um repositório FHIR completo é construído e carregado com pacientes de amostra.
Após a conclusão do processo de construção, inicie o contêiner com o comando:
docker-compose up -d
seguido por
docker ps
Você deverá ver um contêiner chamado iris-fhir em execução. Se o contêiner não iniciar, verifique os logs:
docker logs iris-fhir
5. Instalar a Extensão REST Client do VS Code
Esta extensão permite que você envie requisições à API FHIR diretamente do VS Code.
Abra o VS Code
Vá para Extensões (Ctrl + Shift + X ou Cmd + Shift + X no macOS).
Procure por "REST Client". Existem diversos REST Clients, por favor, instale este:
Clique em Install.
6. Instalar o Python
O Python é necessário para tarefas de programação relacionadas ao FHIR.
Windows
1. Baixe o Python em: https://www.python.org/downloads/2. Execute o instalador e marque a caixa para "Add Python to PATH". Você precisará de credenciais de administrador para fazer modificações no Path.3. Conclua a instalação.4. Verifique a instalação:
python --version
macOS
Abra o Terminal e instale o Python via Homebrew:
Brew install python
Se você não tiver o Homebrew, instale-o primeiro
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Verifique a instalação:
python3 --version
7. Instalar o Jupyter Notebooks
Os Jupyter Notebooks são usados para tarefas de IA e FHIR, e tarefas de FHIR SQL.
Windows & macOS
Abra um terminal (Prompt de Comando, PowerShell ou Terminal do macOS).
Instale o Jupyter usando o pip:
pip install jupyter
jupyter --version
Execute o Jupyter Notebook:
jupyter notebook
Isso abrirá o Jupyter no seu navegador da web.
8. Validação
Execute seu contêiner navegando até o seu arquivo docker-compose no shell. Execute o comando:
docker compose up -d
docker ps
Acesse o Portal de Gerenciamento do IRIS:
Abra seu navegador e vá para:: http://localhost:8080/csp/sys/UtilHome.csp
Credenciais padrão:
Usuário: _SYSTEMSenha: ISCDEMO
Acesse a API FHIR:
Abra seu navegador e vá para: http://localhost:8080/csp/healthshare/demo/fhir/r4/metadata
Verificações Finais
Execute estes comandos para verificar todas as instalações:
code --version # VS Code
git --version # Git
docker --version # Docker
python --version # Python
jupyter --version # Jupyter
Se tudo funcionar, você instalou com sucesso todos os softwares acima.
Solução de problemas
Problema
Solução
"Command not found" para alguma ferramenta
Certifique-se de que ela foi adicionada ao PATH (reinstale se necessário).
Docker não está rodando no Windows
Reinicie o Docker Desktop e certifique-se de que o backend WSL 2 está habilitado.
Contêiner IRIS falha ao iniciar
Execute docker logs iris-fhir para verificar os erros.
Não consigo acessar a API FHIR
Certifique-se de que o contêiner está rodando (docker ps).
Obrigado pelo seu tempo. Aguardo ansiosamente a leitura dos seus comentários!
Artigo
Vinicius Maranhao Ribeiro de Castro · Jul. 6, 2021
Introdução
Suponha que você desenvolveu uma nova aplicação utilizando a parte de Interoperabilidade do InterSystems IRIS e você tem certeza de que será um sucesso! No entanto, você ainda não tem um número concreto de quantas pessoas irão utilizá-la. Além disso, pode haver dias específicos em que há mais pessoas utilizando sua aplicação e dias em que quase ninguém irá acessar. Deste modo, você necessita de que sua aplicação seja escalável!
O Kubernetes já nos ajuda bastante nesta tarefa com um componente chamado Horizontal Pod Autoscaler, que permite escalar horizontalmente uma aplicação baseado em uma métrica específica. Através do componente “Metrics Server” do próprio Kubernetes, é possível obter métricas como utilização de CPU ou de memória. Mas, e se você necessitar escalar sua aplicação utilizando outra métrica, como por exemplo o tamanho da fila de todos os “Business Hosts” presentes em uma determinada Produção da Interoperabilidade do IRIS? Por se tratar de uma métrica customizada, será necessário desenvolver um serviço que irá expor essa métrica customizada para o Kubernetes.
Nesta série de artigos, veremos em mais detalhes como funciona o processo de escalonamento horizontal do Kubernetes. Na parte 1, será abordado o funcionamento do componente responsável pelo escalonamento horizontal do Kubernetes e a criação e exposição de uma métrica customizada do IRIS no formato esperado pelo Prometheus. Já na parte 2, abordaremos como configurar o Prometheus para ler essa métrica e como configurar o prometheus-adapter para expor essa métrica ao Kubernetes.
O projeto utilizado para este artigo está totalmente disponível no GitHub em:
https://github.com/vmrcastro/iris-autoscale
Prometheus – Configurando a leitura destas métricas
Na parte 1 deste artigo, criamos e expusemos uma métrica customizada no IRIS. Agora, necessitamos configurar o Prometheus para ler essa métrica e armazenar em seu banco de dados. No repositório “iris-autoscale” do GitHub que estamos utilizando, já há um arquivo yaml pronto para fazer o deploy do Prometheus já com as configurações necessárias, no seguinte diretório:
prometheus/manifests/00-prometheus-deploy.yml
A parte mais importante deste arquivo yaml é o ConfigMap. Nele é onde definimos o arquivo prometheus.yml que contém as configurações do Prometheus. Este é o conteúdo do arquivo prometheus.yml:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
metrics_path: '/api/monitor/metrics'
# scheme defaults to 'http'.
kubernetes_sd_configs:
- role: pod
relabel_configs:
# Example relabel to scrape only pods that have
# "example.io/should_be_scraped = true" annotation.
# - source_labels: [__meta_kubernetes_pod_annotation_example_io_should_be_scraped]
# action: keep
# regex: true
#
# Example relabel to customize metric path based on pod
# "example.io/metric_path = <metric path>" annotation.
# - source_labels: [__meta_kubernetes_pod_annotation_example_io_metric_path]
# action: replace
# target_label: __metrics_path__
# regex: (.+)
#
# Example relabel to scrape only single, desired port for the pod
# based on pod "example.io/scrape_port = <port>" annotation.
# - source_labels: [__address__, __meta_kubernetes_pod_annotation_example_io_scrape_port]
# action: replace
# regex: ([^:]+)(?::\d+)?;(\d+)
# replacement: $1:$2
# target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
Não iremos abordar aqui todos os parâmetros definidos, até mesmo porque muitos já possuem um comentário autoexplicativo. No entanto, se restar alguma dúvida, é possível consultar a documentação oficial do Prometheus disponível em:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/
Vale ressaltar, no entanto, alguns parâmetros. Em “metrics_path”, vamos indicar o caminho da URL que o Prometheus irá enviar a requisição para buscar as métricas. Portanto, definimos “/api/monitor/metrics”. Desta forma, só nos resta indicar ao Prometheus qual é, ou quais são os endereços e portas do IRIS. No entanto, não podemos especificar um valor fixo para o hostname do(s) POD(s) do IRIS, já que eles não serão fixos! Lembrem-se que os PODs serão adicionados automaticamente mediante um aumento da fila e serão removidos também automaticamente mediante uma diminuição da fila. Por sorte, o Prometheus tem a capacidade de chamar as APIs do Kubernetes para buscar quais são os PODs presentes no cluster naquele momento, ficando sempre sincronizado com o estado do cluster de Kubernetes. Por isso que utilizamos a seção “kubernetes_sd_configs” na configuração do Prometheus. Como o Prometheus rodará no mesmo cluster de Kubernetes que estarão rodando os PODs que desejamos monitorar, não precisamos especificar nenhuma informação para o Prometheus de como se comunicar com o Kubernetes, tudo isso já será detectado automaticamente.
Outro ponto importante: quando tivermos com várias instâncias do IRIS rodando, cada uma delas terá sua métrica “queue_size”. Como saberemos no Prometheus qual instancia determinada métrica “queue_size” está se referindo? Podemos solucionar isso fazendo com que o Prometheus adicione em cada métrica um “label” para identificar de qual POD a métrica está se referindo. Por padrão, para cada POD que o Prometheus descobriu no cluster Kubernetes, há uma série de “meta labels” que podem ser incorporados nas métricas, através de configurações de “relabeling”. É isso que as seguintes linhas do arquivo de configuração do Prometheus fazem:
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
Neste caso, estamos adicionando para cada métrica, um label que se chama “kubernetes_namespace” que contém o nome do namespace do Kubernetes em que o POD está rodando e um label denominado “kubernetes_pod_name” que contém o nome do POD.
Com o Prometheus rodando, é possível verificar se as configurações estão corretas. Basta acessar a seguinte URL no seu browser:
http://<IP_do_Prometheus>:<Porta_do_Prometheus>/graph
Obs: Por padrão, é criado um “service” do Kubernetes do tipo Load Balancer para acessar o Prometheus. Você pode descobrir o IP do Prometheus verificando o campo “External IP” do service “prometheus-svc”.
É possível então, escrever o nome da métrica “queue_size” no campo de busca e clicar no botão “Execute”.
Ao clicar na aba “Graph”, se estiver tudo certo, você deverá visualizar uma linha amarela no valor 0, como mostra a figura abaixo.
Prometheus-Adapter - expondo métricas ao Kubernetes
Com o Prometheus configurado e coletando as métricas dos PODs do IRIS, agora só nos resta configurar nosso “metric API server”, no nosso caso o prometheus-adapter, para expor essas métricas de uma maneira que o Kubernetes entenda. Assim como no deploy do Prometheus, o deploy do prometheus-adapter no Kubernetes já está pronto, sendo realizado através dos arquivos yaml no diretório prometheus/manifests/ no repositório “iris-autoscale”. No entanto, vale a pena comentarmos alguns detalhes.
Primeiramente, para o prometheus adapter funcionar, é necessário especificar qual é o endereço e porta do Prometheus. Isso é especificado no arquivo “custom-metrics-apiserver-deployment.yaml”, no parâmetro “prometheus-url” na seção “args” do container “custom-metrics-apiserver”.
Assim como no deploy do Prometheus, as configurações do prometheus-adapter se encontram em um “configMap”, definido dentro do arquivo “custom-metrics-config-map.yaml”. Dentro do “configMap”, é definido o arquivo “config.yaml” que contém as seguintes configurações:
rules:
- seriesQuery: 'queue_size{kubernetes_namespace!="",kubernetes_pod_name!=""}'
resources:
overrides:
kubernetes_namespace: {resource: "namespace"}
kubernetes_pod_name: {resource: "pod"}
metricsQuery: '<<.Series>>{<<.LabelMatchers>>}'
Como é possível observar, em “seriesQuery” é definido a consulta que será enviada ao Prometheus para descobrir quais são as métricas disponíveis. Como neste exemplo só vamos trabalhar com a métrica “queue_size”, vamos especificá-la diretamente. Portanto, nesta query estamos buscando todas as métricas denominadas “queue_size” disponíveis, desde que “kubernetes_namespace” e “kubernetes_pod_name” não sejam nulos. Na seção “overrides”, vamos especificar ao prometheus-adapter de qual label deve ser extraído o namespace do Kubernetes e o nome do POD. Finalmente, em “metricsQuery”, vamos especificar qual é a consulta que desejamos enviar ao Prometheus para obter o valor da métrica a ser exposta para o Kubernetes. É possível, por exemplo, utilizar alguma função de agregação para transformar o valor antes de expô-la ao Kubernetes. No entanto, no nosso caso, queremos expor a métrica exatamente como ela se encontra no Prometheus, por isso especificamos apenas
metricsQuery: '<<.Series>>{<<.LabelMatchers>>}'
Ao final desta etapa é possível testar se está tudo certo simulando a requisição que o Horizontal Pod Autoscaler faria para a Custom Metrics API através do seguinte comando:
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/queue_size"
A resposta esperada seria algo do parecido com o seguinte:
{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/%2A/queue_size"
},
"items": [{
"describedObject": {
"kind": "Pod",
"namespace": "default",
"name": "iris-autoscale-5d4b546854-79gs7",
"apiVersion": "/v1"
},
"metricName": "queue_size",
"timestamp": "2021-05-03T18:31:03Z",
"value": "0",
"selector": null
}]
}
InterSystems IRIS - Produção criada para testes
A produção criada neste projeto é uma produção bem simples, apenas para testes. A ideia geral é expor um endpoint REST que ao receber uma requisição, envia uma mensagem assíncrona para qualquer um dos três Business Operations disponíveis (denominados “Teste 1”, “Teste 2” e “Teste 3”) de maneira randômica. Esses “Business Operations” são bem simples e, a única coisa que eles fazem é um “Hang” de 0 a 30 segundos, também de maneira randômica. Obviamente, essa produção não faz nenhum sentido, a ideia aqui é apenas simular o enfileiramento de mensagens no IRIS.
A requisição que deve ser enviada ao IRIS é a seguinte:
$ curl 'http://<IP_do_IRIS>:<Porta_WebServer_IRIS>/api/autoscale/request'
Se der tudo certo, a seguinte resposta retornará juntamente com um código HTTP status de “200 – OK”:
{"responseText":"The request has been sended!"}
Fazendo o deploy e testando o projeto
Para fazer o deploy do projeto em seu cluster de Kubernetes, basta clonar o repositório “iris-autoscale”, indicado na Introdução deste artigo e executar os seguintes comandos a partir da raiz do repositório clonado:
$ kubectl apply -f prometheus/manifests
$ kubectl apply -f kubernetes.yml
Para verificar se tudo correu bem, vamos dar uma olhada nos pods. Verifique se os seguintes PODs estão presentes com status de “Running” e com “READY 1/1”:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
iris-autoscale-5b9b77577f-jb2z4 1/1 Running 0 119m
prometheus-b974955c5-jwrhd 1/1 Running 0 119m
$ kubectl get pods -n custom-metrics
NAME READY STATUS RESTARTS AGE
custom-metrics-apiserver-776cf7cb8b-4v5b5 1/1 Running 0 121m
Se tudo correu bem, só nos resta testar o escalonamento automático. Para isso, utilizaremos o Collection Runner do PostMan para ficar enviando várias requisições ao Service do Kubernetes “iris-autoscale-svc”. Vamos enviar um total de 3000 requisições com um espaçamento de 2000 ms entre elas:
Podemos acompanhar as filas crescendo através da interface do Prometheus do lado esquerdo da tela e novos PODs do IRIS sendo criados e deletados automaticamente, como mostra o vídeo a seguir:
Conclusão
Neste artigo, abordamos todos os componentes necessários para escalar horizontalmente o InterSystems IRIS utilizando uma métrica customizada, no nosso caso a métrica “queue_size” que nos informa qual é o tamanho total das filas de uma produção em um namespace do IRIS. Por se tratar de uma métrica customizada, foi necessário fazer o deploy e configurar o Prometheus, para coletar essa métrica de cada instância de IRIS em execução, e o prometheus-adapter, para expor essa métrica no formato esperado pelo Kubernetes. Com isso, foi possível definir essa métrica na configuração do Kubernetes Horizontal Pod Autoscaler para que o escalonamento de nossas instâncias de IRIS seja realizado automaticamente.
Referências
https://learnk8s.io/autoscaling-apps-kubernetes
https://prometheus.io/docs/introduction/first_steps/
https://prometheus.io/docs/prometheus/latest/configuration/configuration/
https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/config-walkthrough.md
https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/walkthrough.md
https://github.com/prometheus/prometheus/blob/release-2.26/documentation/examples/prometheus-kubernetes.yml
Anúncio
Cristiano Silva · Out. 25, 2022
Eu gostaria de anunciar o lançamento de algo realmente bastante interessante - revolucionário na verdade. Isso pode soar exagerado, mas acho que você não viu nada parecido com isso, ou mesmo pensou que fosse possível!
Lançamos um novo módulo JavaScript/Node.js chamado glsdb, mais informações:
https://github.com/robtweed/glsdb
No entanto, para os propósitos deste anúncio aqui, quero apenas focar em uma parte do glsdb: suas APIs que abstraem classes IRIS (ou Caché) como objetos JavaScript equivalentes.
Vamos dar uma olhada rápida no que quero dizer com isso, porque quero dizer Objetos JavaScript que na verdade são Objetos IRIS que residem no banco de dados!
Suponha que eu instalei os dados SAMPLES do repositório InterSystems, por exemplo, conforme descrito aqui:
https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ASAMPLES
Então, para acessar esses dados SAMPLES usando o glsdb, primeiro preciso abrir uma conexão com o IRIS (o glsdb usa nossa interface mg-dbx para fazer essa conexão), por exemplo:
const {glsDB} = await import('glsdb');
let glsdb = new glsDB('iris');
let options = {
connection: 'network',
host: '172.17.0.2',
tcp_port: 7042,
username: "_SYSTEM",
password: "xxxxxx",
namespace: "SAMPLES"
}
glsdb.open(options);
Agora que fiz isso, posso fazer o seguinte para acessar um registro de Funcionário (Employee) no banco de dados SAMPLES:
let Employee = glsdb.irisClass('Sample.Employee');
let employee = Employee(101);
Até agora, tão surpreendente, talvez.
Mas é aqui que começa a diversão alucinante. Criamos um objeto JavaScript chamado employee que parece representar uma instância de Funcionário (Employee) com o OID 101, mas na verdade é um objeto JavaScript bastante especial: é um JavaScript Proxy Object que é mapeado diretamente para a instância física da classe IRIS no banco de dados. Eu tenho acesso direto a todas as propriedades e métodos da Classe IRIS, e quando eu manipulo o Proxy Object, na verdade estou manipulando a instância da Classe IRIS no banco de dados! Então agora eu posso fazer coisas como essa:
let salary = employee.Salary;
console.log('employee.Salary = ' + salary);
let company = employee.Company;
let name = company.Name;
console.log('Company: ' + name);
O importante a ser percebido aqui é que meu objeto employee não é uma cópia JavaScript na memória da instância da Classe IRIS - é a instância real da Classe Class, no banco de dados!
E como você pode ver acima, posso até encadear o relacionamento entre o Funcionário (Employee) e a Empresa (Company) como você faria em ObjectScript. Na verdade, eu posso até fazer isso de uma só vez:
console.log(employee.Company.Name);
e encadeie diretamente pelas propriedades, diretamente na Classe Física IRIS no banco de dados!
Eu também posso salvar as alterações no objeto - para isso eu uso um método especial chamado _save(), ex.: employee._save()
Para criar uma nova instância de um funcionário, eu simplesmente não especifico um OID, ex.:
let newEmployee = Employee();
glsdb fornece um atalho para definir e salvar um novo registro de uma só vez, usando o método _set(), ex.:
let ok = newEmployee._set({
Title: 'Manager'
Salary: 60000
})
Você pode até mesmo inspecionar os métodos e propriedades disponíveis da Classe:
console.log(employee._properties);
console.log(employee._methods);
Então, essa é uma visão geral do glsdb: espero que dê um gostinho de como é insano!
Você provavelmente está se perguntando como é possível fazer esse tipo de coisa.
Bem, em resumo, tudo se resume a um recurso relativamente novo do JavaScript: objetos proxy. Objetos Proxy são objetos especiais que atuam, como o próprio nome indica, como um proxy para outro objeto real. Mas o verdadeiro poder deles está no fato de que você pode definir "armadilhas" para acessar ou alterar as propriedades do Objeto Proxy e (e essa é a chave) você pode aplicar lógica personalizada a essas armadilhas. No caso do glsdb, essa lógica personalizada usa as APIs mg-dbx nos bastidores para realizar o acesso equivalente ou a alteração que está acontecendo no objeto Proxy para uma classe IRIS correspondente.
De qualquer forma, esse é um vislumbre rápido do mundo louco possível com o glsdb! O que eu publiquei é uma versão antecipada, então tenho certeza de que há aspectos do proxy IRIS que precisarão de ajustes. Se você quiser experimentá-lo e ver o que ele pode fazer, por favor, avise-me se você encontrar algo que não funcione adequadamente.
Ah, e a propósito, confira as outras APIs que o glsdb fornece - elas são igualmente interessantes e alucinantes!
E, finalmente, glsdb é um software de código aberto, então, por favor, brinque com o código e poste PRs no repositório do Github se você quiser ajudar no desenvolvimento posterior.
Artigo
Heloisa Paiva · Abr. 14
Introdução
O desempenho do banco de dados tornou-se um fator crítico de sucesso em um ambiente de aplicações moderno. Portanto, identificar e otimizar as consultas SQL que consomem mais recursos é essencial para garantir uma experiência de usuário fluida e manter a estabilidade da aplicação.
Este artigo explorará uma abordagem rápida para analisar as estatísticas de execução de consultas SQL em uma instância InterSystems IRIS para identificar áreas de otimização dentro de uma macro-aplicação.
Em vez de focar no monitoramento em tempo real, configuraremos um sistema que coleta e analisa estatísticas pré-calculadas pelo IRIS uma vez por hora. Essa abordagem, embora não permita o monitoramento instantâneo, oferece um excelente compromisso entre a riqueza de dados disponíveis e a simplicidade de implementação.
Usaremos o Grafana para visualização e análise de dados, o InfluxDB para armazenamento de séries temporais e o Telegraf para coleta de métricas. Essas ferramentas, reconhecidas por seu poder e flexibilidade, nos permitirão obter uma visão clara e explorável.
Mais especificamente, detalharemos a configuração do Telegraf para recuperar estatísticas. Também configuraremos a integração com o InfluxDB para armazenamento e análise de dados, e criaremos dashboards personalizados no Grafana. Isso nos ajudará a identificar rapidamente as consultas que exigem atenção especial.
Para facilitar a orquestração e o deploy desses vários componentes, empregaremos o Docker.
Pré-requisitos
Before you start, make sure you have the following:
Git: O Git é necessário para clonar o repositório do projeto que contém os arquivos de configuração e scripts..
Docker ou Docker Desktop: O Docker pode ser usado para conteinerizar as aplicações InfluxDB, Telegraf e Grafana, tornando-as mais fáceis de implantar e gerenciar.
InterSystems IRIS instance:Pelo menos a versão 2022.1, idealmente 2023.1 ou superior, com o pacote sql-stats-api instalado. Este pacote é essencial para expor as estatísticas SQL do IRIS e permitir que o Telegraf as colete. [ [Link to OpenExchange]
Embora nosso docker-compose inclua uma instância IRIS, ela não conterá nenhum dado de estatísticas SQL, pois será construída e iniciada do zero. Portanto, não será uma escolha conveniente para testar o sistema. É por isso que recomendamos fortemente que você tenha outra instância IRIS "ativa" (aquela com um histórico de consultas SQL) para ter a capacidade de visualizar dados reais e testar a ferramenta de análise.
Sobre as estatísticas
O IRIS coleta estatísticas de execução de consultas SQL com granularidade horária e diária. As estatísticas horárias identificam variações de desempenho ao longo do dia, enquanto as estatísticas diárias fornecem uma visão geral da atividade do banco de dados.
Abaixo, você pode ver os dados que coletaremos para cada consulta SQL:
Número de execuções: Indica o número de vezes que a consulta foi executada.
Tempo total de execução: Mede o tempo total de execução da consulta.
Variância dos tempos de execução: É usada para identificar variações de desempenho e problemas isolados.
Número total de linhas retornadas (RowCount): Disponível para IRIS 2023.1 e superior, esta métrica indica o número total de linhas retornadas pela consulta. Pode ajudá-lo a identificar consultas com alto consumo de recursos.
Número total de comandos executados: Também disponível para IRIS 2023.1 e superior, esta métrica facilita uma análise mais detalhada da atividade do banco de dados e identifica consultas que poderiam ser otimizadas pela redução do número de operações.
Essas informações podem ser acessadas através das seguintes tabelas:
INFORMATION_SCHEMA.STATEMENT_DAILY_STATS
INFORMATION_SCHEMA.STATEMENT_HOURLY_STATS
Essas tabelas estão disponíveis desde o IRIS 2022.1. Abaixo, apresentamos um exemplo de consulta SQL para recuperar as estatísticas:
SELECT ds.*
FROM INFORMATION_SCHEMA.STATEMENT_DAILY_STATS ds
INNER JOIN INFORMATION_SCHEMA.STATEMENTS st On ds.Statement = st.Hash
SELECT DATEADD('hh',"Hour",$PIECE(hs."Day",'||',2)) As DateTime, hs.*
FROM INFORMATION_SCHEMA.STATEMENT_HOURLY_STATS hs
INNER JOIN INFORMATION_SCHEMA.STATEMENTS st On $PIECE(hs."Day",'||',1) = st.Hash
Para versões anteriores ao IRIS 2022.1, recomendo fortemente o artigo de David Loveluck, que explica como recuperar estatísticas semelhantes.
Arquitetura
O projeto é baseado na interação de quatro componentes chave: IRIS, Grafana, InfluxDB e Telegraf. O diagrama abaixo ilustra a arquitetura geral do sistema e o fluxo de dados entre os vários componentes:
InterSystems IRIS:É a instância que utilizaremos para recuperar as estatísticas.
Pacote sql-stats-api: Este pacote ObjectScript expõe os dados estatísticos do IRIS via API REST. Ele oferece dois formatos de saída: JSON para uso geral e Line Protocol, um formato otimizado para ingestão rápida de dados de séries temporais no InfluxDB.
Telegraf: É um agente de coleta de métricas que fornece a ligação entre o IRIS e o InfluxDB. Neste projeto, utilizaremos duas instâncias do Telegraf:
Um agente consulta periodicamente a API REST do IRIS para recuperar as estatísticas SQL em tempo real.
Outro agente opera no modo de "varredura de diretório". Ele monitora um diretório com arquivos armazenados e os transmite para o InfluxDB, permitindo a integração de dados inacessíveis via API REST.
InfluxDB: Este banco de dados de séries temporais armazena e gerencia as estatísticas SQL coletadas pelo Telegraf, já que sua arquitetura é otimizada para esse tipo de dado. O InfluxDB também oferece integração nativa com o Grafana, facilitando a visualização e análise dos dados. Escolhemos o InfluxDB em vez do Prometheus porque o último é mais voltado para o monitoramento em tempo real e não é adequado para armazenar dados agregados, por exemplo, somas ou médias por hora ou dia, que são essenciais para nossa análise.
Grafana: É uma ferramenta de visualização que permite criar dashboards personalizados e interativos para analisar o desempenho do SQL. Ele recupera dados do InfluxDB e oferece uma variedade de gráficos e widgets para visualizar as estatísticas de forma clara e utilizável.
Instalação
Comece clonando o repositório:
git clone https://github.com/lscalese/iris-sql-dashboard.git
cd irisiris-sql-dashboard
Configurando o ambiente
Este projeto usa o Docker para orquestrar o Grafana, InfluxDB, Telegraf e IRIS. Por razões de segurança, informações confidenciais, como chaves de API e senhas, são armazenadas em um arquivo .env.
Crie o arquivo .env usando o exemplo fornecido abaixo:
cp .env.example .env
Edite o arquivo .env para configurar as variáveis:
Configuração das variáveis
TZ:Fuso horário. Você deve modificar esta variável de acordo com seu fuso horário para garantir que os dados tenham o carimbo de data/hora correto.
DOCKER_INFLUXDB_INIT_PASSWORD: Esta é a senha do Administrador para acessar o InfluxDB.
IRIS_USER: É um usuário IRIS da instância Docker do IRIS (_system por padrão).
IRIS_PASSWORD :É a senha da instância Docker do IRIS (SYS por padrão).
As chaves de API permitem as seguintes conexões:
GRAFANA_INFLUX_API_KEY : Grafana <-> InfluxDB.
TELEGRAF_INFLUX_API_KEY : Telegraf <-> InfluxDB.
Geração de Chaves de API
Por razões de segurança, o InfluxDB requer chaves de API para autenticação e autorização. Essas chaves são usadas para identificar e autorizar vários componentes (Telegraf, Grafana) a acessar o InfluxDB.
O script init-influxdb.sh, incluído no repositório, facilita a geração dessas chaves. Ele será executado automaticamente na primeira vez que o contêiner inflxudb2 for iniciado:
docker compose up -d influxdb2
Após alguns segundos, o arquivo .env será atualizado com suas chaves de API geradas.
Nota: esta etapa só deve ser realizada na primeira vez que você iniciar o contêiner..
Verifique se você tem acesso à interface de administração do InfluxDB através da URLhttp://localhost:8086/
Faça login com o nome de usuário "admin" e a senha especificada na variável de ambiente“DOCKER_INFLUXDB_INIT_PASSWORD” no arquivo ".env". Ao navegar em "Load Data >> Buckets", você deverá encontrar um bucket "IRIS_SQL_STATS" pré-configurado.
Ao acessar "Load Data >> API Tokens", você deverá encontrar nossas 2 chaves de API: "Grafana_IRIS_SQL_STATS" e "Telegraf_IRIS_SQL_STATS":
O ambiente está pronto agora e podemos passar para o próximo passo!
Início
Como o ambiente foi configurado e as chaves de API foram geradas, você pode finalmente iniciar o conjunto de contêineres. Para fazer isso, execute o seguinte comando no diretório raiz do projeto:
docker compose up -d
Este comando iniciará todos os serviços definidos no arquivo docker-compose.yml em segundo plano: InfluxDB, Telegraf, Grafana e a instância IRIS.
Dashboard Grafana
O Grafana agora está disponível em http://localhost:3000.
Conectar ao Grafana
Abra seu navegador da web e acesse http://localhost:3000. O nome de usuário e a senha padrão são admin/admin. No entanto, você será solicitado a alterar a senha no primeiro login.
Verificando a fonte de dados InfluxDB
A fonte de dados InfluxDB já está pré-configurada no Grafana. Tudo o que você precisa fazer é verificar se ela está funcionando corretamente.
Vá para "Connections > Data sources".
Você deverá ver uma fonte de dados chamada "influxdb".
Clique nela para modificá-la. Em seguida, clique em "Save & Test". A mensagem "Datasource is working. 1 bucket found" deverá aparecer na tela agora.
Explorando os painéis
Neste ponto, você verificou que a comunicação entre o Grafana e o InfluxDB está estabelecida, o que significa que você pode explorar os painéis predefinidos.
Prossiga para "Dashboards".
Você encontrará dois painéis predefinidos:
InfluxDB - SQL Stats: Este painel exibe estatísticas gerais de execução de consultas SQL, por exemplo, número de execuções, tempo total de execução e variação do tempo de execução.
InfluxDB - SQL Stats Details: Este painel fornece informações mais detalhadas sobre cada consulta SQL, por exemplo, o número total de linhas retornadas ou comandos executados.
Por que os painéis estão vazios?
Se você abrir os painéis, notará que eles estão vazios. Isso ocorre porque nosso agente Telegraf está atualmente conectado à instância IRIS fornecida no repositório Docker, que não contém dados estatísticos em suas tabelas. As estatísticas SQL são coletadas apenas se a instância IRIS estiver ativa e mantiver um histórico de consultas SQL.
Na próxima seção, exploraremos como injetar dados na instância IRIS para visualizar as estatísticas no Grafana.
Telegraf
O sistema de monitoramento opera dois agentes Telegraf com funções específicas:
telegraf-iris.conf: Este agente coleta dados em tempo real de uma instância IRIS ativa. Ele consulta a API REST do IRIS para recuperar estatísticas SQL e enviá-las para o InfluxDB.
telegraf-directory-scan.conf: Este agente integra dados históricos armazenados em arquivos. Ele monitora o diretório ./telegraf/in/, lê arquivos contendo estatísticas SQL e os envia para o InfluxDB.
Para coletar dados em tempo real, você deve conectar o Telegraf a uma instância IRIS ativa com o pacote sql-stats-api instalado. Este pacote expõe as estatísticas SQL por meio de uma API REST, permitindo que o Telegraf as acesse.
Configurando o telegraf-iris.conf
Para conectar o Telegraf à sua instância IRIS, você deve modificar o arquivo./telegraf/config/telegraf-iris.conf. Abaixo, você pode encontrar um exemplo da configuração:
[[inputs.http]]
## One or more URLs from which to read formatted metrics
urls = [
"http://iris:52773/csp/sqlstats/api/daily",
"http://iris:52773/csp/sqlstats/api/hourly"
]
## HTTP method
method = "GET"
## Optional HTTP headers
headers = {"Accept" = "text/plain"}
## Optional HTTP Basic Auth Credentials
username = "${IRIS_USER}"
password = "${IRIS_PASSWORD}"
data_format = "influx"
Certifique-se de que${IRIS_USER} e ${IRIS_PASSWORD}estejam definidos corretamente no seu arquivo.env .Nota: Você pode copiar o arquivo e editar os parâmetros para conectar o Telegraf a várias instâncias IRIS.
Reinicie o Telegraf:
Após modificar o arquivo de configuração, você precisa reiniciar o contêiner Telegraf para que as alterações tenham efeito:
docker compose up -d telegraf --force-recreate
Recuperando dados históricos
Para recuperar estatísticas históricas de SQL, use o método CreateInfluxFile do ObjectScript na sua instância IRIS:
; Adapt the path to your needs
Set sc = ##class(dc.sqlstats.services.SQLStats).CreateInfluxFile("/home/irisowner/dev/influxdb-lines.txt",,1)
Este script gravará o histórico de estatísticas de SQL em arquivos de texto, com um comprimento máximo de 10.000 linhas por arquivo. Em seguida, você pode colocar esses arquivos no diretório ./telegraf/in/ para processá-los e injetá-los no InfluxDB.
Verificando a injeção de dados
Você pode verificar se os dados foram injetados com precisão no InfluxDB com a ajuda da interface web. Vá para o "Data Explorer" e confira:
Visualização de dados no Grafana
Uma vez que os dados tenham sido injetados, você poderá visualizá-los nos seus painéis Grafana provisionados.
Chegamos ao final deste artigo. Espero que tenha sido útil para você e que tenha ensinado como configurar facilmente um sistema para monitorar e analisar estatísticas de SQL em suas instâncias IRIS.
Como você deve ter notado, este artigo se concentrou nos aspectos práticos da configuração e do emprego de várias ferramentas. Não pesquisamos todos os detalhes do funcionamento interno do InfluxDB, o formato do Line Protocol ou a linguagem de consulta Flux, nem examinamos a infinidade de plugins disponíveis para o Telegraf.
Esses tópicos, por mais fascinantes que sejam, exigiriam um artigo muito mais longo. Eu o(a) encorajo fortemente a consultar a documentação oficial para InfluxDB Começar com o InfluxDB e o Diretório de Pluginsdo Telegraf para aprofundar seu conhecimento e descobrir todas as possibilidades oferecidas por essas ferramentas.
Não hesite em compartilhar suas experiências nos comentários.
Obrigado por ler e até a próxima!
Artigo
Jhonata Rebouças · Maio 1, 2021
O mesmo serviço com a possibilidade de receber várias consultas SQL diferentes e sempre entregar o resultado independente de quantas colunas distintas tenham essas diferentes consultas. Aqui demonstro como pode ser possível montar esse tipo de serviço utilizando o Service Bus da Intersystems.
Possível cenário (Desconsiderar o uso de um BI):
Vamos pensar em um painel real time onde iremos fornecer as informações de consumo de um material por região para o setor de compras e teremos as informações do nome do produto, fabricante e quantidade por exemplo.
O problema começa quando pensamos que esse painel será modularizado e posteriormente teremos a necessidade de atender aos outros setores que buscam informações totalmente diferentes, afinal um setor como relacionamento pode necessitar de informações relacionadas aos clientes. Nesse caso, criar um novo serviço totalmente do zero seria um grande desperdício de recurso e consequentemente financeiro.
Agora que temos o cenário podemos pensar em uma solução e no caso vamos utilizar o Ensemble da intersystems.O payload deve ser pensado para expandir e suportar o máximo de colunas possíveis imaginando que um momento pode atender a uma demanda de duas colunas e em outra pode atender a uma demanda de nove ou mais colunas.A sugestão é adicionar essas colunas em um array e seus dados em outro array relacionado dessa forma pode haver uma flexibilização em sua estrutura.
{
"columns":[
{
"description": "String", //Nome da coluna
"data": [
{
"value": "String" //Valor da coluna
}
]
}
]
}
Utilizando o Ensemble e SQL:
Vamos definir a montagem dos objetos e mensagens que serão necessários para criar um payload semelhante, com a adição de algumas informações que serão úteis a identificação do painel, mas podem ser eliminadas ou adaptadas dependendo da necessidade.Será adicionado informações relacionadas a identificação e descrição do painel que pode ser útil no layout.Não será abordado o passo a passo da criação dos objetos e mensagens pois não possuem nada de diferente do padrão. *disponível no git hub
O Business Operation
Possui um pequeno detalhe que fará a grande diferença. Não é necessário se preocupar com as informações adicionais sobre o painel, somente com a consulta SQL. No exemplo usamos uma consulta direta na base do caché, porém é possível utilizar bancos como Oracle, MySql, SqlServer e outros que pode inclusive utilizar Stored Procedures que se torna mais organizado e eficiente.Ao realizar a consulta é interessante ter conhecimento de quantas colunas essas esse painel retornará e a consulta deverá respeitar esse valor e ordem para que seja montado um retorno correto.
For i=1:1:pRequest.columns {
Set sc=result.Execute()
While result.Next(.sc) {
Set tData = ##class(IRIS.JHONATA.OBJ.Reports.Row.row).%New()
Set tData.row = result.Get("row"_i)
Do pResponse.data.Insert(tData)
}
}
Note que o resultado da consulta é esperado como um alias sequenciado, result.Get("row"_i), comparando com a quantidade de colunas garantimos que todos os dados serão recebidos de forma ordenada antes de passar para a próxima coluna.A consulta ficará semelhante:
select name as row1,
code as row2,
description as row3
from table
No Operation é interessante receber a procedure ou query como propriedade no request, isso porque poderemos enviar essa informação amarrada a uma regra criada posteriormente e não teremos a necessidade de alterar a classe ou mesmo reiniciar a cada novo painel criado.
O Business Process
No process temos bastantes detalhes para permitir que tenhamos a adição de novas consultas para os painéis que por ventura forem surgindo sem a necessidade de interferir no que já está em produção. Para não ficar ainda mais extenso, vamos focar somente em algumas atividades principais.Utilizamos um foreach para percorrer os resultados e contabilizar o seu número total de resultados que será importante para identificar em que momento a informação passa a ser pertencente à próxima coluna.
Outro ponto importante é a utilização de um Data Transformation para organizar a lista de resultados em colunas com seus respectivos dados associados à coluna correta.
Para que não seja necessário parar o serviço ou impactar seu uso a cada nova funcionalidade ou painel toda parte de consulta ou procedure deve ficar em uma regra identificada pelo tópico do projeto, essa será a única informação necessária no request. Nessa regra pode estar contido informações estáticas do serviço como descrição e nome do painel.O nome das colunas também devem estar presentes na regra em uma única string com delimitadores e na mesma ordem das colunas retornadas na query para que possa ser contado e utilizado na lógica do Data transformation.
Resultado final:
Pode ser utilizado de várias maneiras, por exemplo através de um serviço REST que informará somente o TOPICO desejado.Na demonstração existe somente uma regra com uma consulta realizada no caché, e em seguida é gerada uma nova regra com uma nova consulta que já entra pronto para funcionar sem interrupção dos demais.
O Projeto
Todo o projeto pode ser baixado no GIT HUB https://github.com/jhonatabf/iris_universal_report e a tabela de exemplo está na pasta OBJ/Persistent