Limpar filtro
Artigo
Heloisa Paiva · Jun. 5
Duas Grandes Mudanças para a Ferramenta de Código Aberto TestCoverage: Suporte a Python Embutido e uma Nova Interface de Usuário
Python Embutido
Anteriormente, o TestCoverage conseguia rastrear a cobertura de testes unitários apenas para códigos escritos em ObjectScript. Ele ignorava o código escrito em outras linguagens, como Python, nas estatísticas de cobertura.
À medida que mais e mais código de aplicativo IRIS está sendo escrito em Python Embutido em vez de apenas ObjectScript, é fundamental que o TestCoverage possa incluir os resultados de cobertura para o código Python Embutido . Clientes (através dos issues do TestCoverage no GitHub), bem como outros na InterSystems, expressaram interesse em ver o suporte ao Python Embutido.
O usuário ainda instala e executa o TestCoverage da mesma forma que antes, conforme descrito no TestCoverage GitHub. TOs resultados de cobertura para código Python Embutido agora são incluídos nas estatísticas de cobertura agregadas, bem como na coloração individual das linhas, como exibido no exemplo acima.
Por baixo do capô, a cobertura do Python Embutido é rastreada usando o tracer sys.settrace do Python, independentemente do %Monitor.System.LineByLine do ObjectScript, e então os resultados são combinados e exibidos juntos. Isso pode causar pequenas discrepâncias em quais linhas são marcadas como executáveis (ou seja, podem ser executadas). Por exemplo, na imagem acima, o Python considera a instrução elif como executável, mas o ObjectScript não considera a instrução ElseIf como executável. No fim das contas, qualquer linha de código marcada em vermelho não foi coberta, e qualquer linha de código marcada em verde ainda foi coberta; isso apenas afeta ligeiramente quais linhas de código ignoramos, o que não causa nenhum problema.
Nova Interface de Usuário do TestCoverage
A interface de usuário anterior do TestCoverage era uma antiga Zen UI que não mostrava muitas das estatísticas úteis que o TestCoverage rastreava. Além disso, o TestCoverage só podia ser executado a partir da linha de comando.
Para resolver esses problemas, criamos uma nova Angular UI baseada no isc.perf.ui, a interface de usuário existente para interagir com o Line-By-Line Monitor (^%SYS.MONLBL). O aplicativo web vem com uma API REST e uma conexão WebSocket para recuperar dados do servidor IRIS. Ele também corrige a autenticação de usuário anterior para o isc.perf.ui, de modo que agora ele usa o login/logout padrão do IRIS. Isso também está publicamente disponível no Open Exchange, sob o nome isc-perf-ui. Abaixo estão algumas das novas funcionalidades e usos da interface de usuário.
Instalação
Existem algumas etapas adicionais para instalar o isc.perf.ui se você quiser usar os novos recursos do TestCoverage. Apenas no Windows, você precisa habilitar o protocolo WebSocket do IIS. Em qualquer sistema operacional, você precisa conceder a um usuário específico (geralmente CSPSystem) uma permissão de recurso (geralmente %DB_User) no portal de gerenciamento do IRIS. Essas etapas são descritas na página do GitHub do isc-perf-ui..
Página de Cobertura de Teste
Na página de Cobertura de Teste, você pode selecionar os parâmetros com os quais deseja executar o TestCoverage em seus testes de unidade.
As explicações dos parâmetros incluem descrições do que cada um deles controla. Você também pode clicar em uma caixa de entrada para ver um valor de exemplo, e há validação de entrada para garantir que seus dados estejam em um formato válido.
Depois de clicar em "enviar", a chamada para executar o TestCoverage será iniciada, e você verá o progresso ao vivo dos seus testes de unidade no registro na parte inferior da página.
Após a conclusão da execução dos testes, o menu suspenso à direita deve abrir com uma lista de combinações de rotina e caminho de teste, bem como a porcentagem geral de cobertura do seu código e o link para os próprios resultados dos testes de unidade.
Clique em qualquer um deles para ser levado à página de resultados de cobertura daquela rotina, dentro do diretório de teste de unidade.
Aqui, você consegue ver quais linhas de código foram cobertas pelos seus testes de unidade, de acordo com o TestCoverage. Há também métricas adicionais, como TempoTotal, que monitora a quantidade de tempo que o código passou em uma determinada linha, do início ao fim da execução.
Você pode ordenar ainda mais em ordem crescente ou decrescente clicando nas setas ao lado dos cabeçalhos; essa é uma forma útil de ver quais linhas de código demoram mais para executar.
Por fim, o botão "Mostrar Métodos" abre uma tabela com a complexidade ciclomática de cada um dos seus métodos, mostrando quais são os mais complexos e vulneráveis a bugs.
Quando terminar, você pode clicar no botão "Voltar" para retornar à página inicial. O botão vermelho "Limpar resultados" permite apagar todas as suas execuções de cobertura de teste.
Página de Cobertura Histórica
Após clicar em um ID de Execução específico de uma execução anterior, você pode visualizar os resultados de cobertura em nível de classe (cobertura de linha, cobertura de método, tempo) para todas as classes daquela execução. Estes são os mesmos dados da página de resultados principal do TestCoverage. Esta tabela também é classificável por cada coluna.
Mais uma vez, ambas as ferramentas estão disponíveis no InterSystems Open Exchange (isc-perf-ui e Test Coverage Tool) e no GitHub. Bons testes a todos!
Artigo
Angelo Bruno Braga · Fev. 25, 2022
Neste artigo iremos construir uma configuração IRIS de alta disponibilidade utilizando implantações Kubernetes com armazenamento persistente distribuído substituindo o "tradicional" espelhamento IRIS. Esta implantação será capaz de tolerar falhas relacionadas a infraestrutura como falhas em nós, armazenamento e de Zonas de Disponibilidade. A abordagem descrita reduz muito a complexidade da implantação em detrimento um objetivo de tempo de recuperação (RTO) ligeiramente estendido.Figura 1 - Espelhamento Tradicional x Kubernetes com Armazenamento Distribuído
Todos os códigos fonte deste artigo estão disponíveis em https://github.com/antonum/ha-iris-k8s TL;DR
Assumindo que você possui um cluster com 3 nós e também uma certa familiaridade com o Kubernetes – vá em frente:
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml
Se você não tem certeza sobre o que as duas linhas acima fazem ou não possui o sistema onde executá-las - pule para a seção “Requisitos de Alta Disponibilidade". Iremos explicar tudo detalhadamente conforme evoluirmos no artigo.
Esta primeira linha instala o Longhorn - armazenamento distribuído Kubernetes de código aberto. A segunda instala o InterSystems IRIS, utilizando um volume baseado no Longhorn para o Durable SYS.
Aguarde até que todos os pods atinjam o estado de "em execução". kubectl get pods -A
Agora você deve ser capaz de acessar o portal de administração do IRIS em http://<IP Público do IRIS>:52773/csp/sys/%25CSP.Portal.Home.zen (a senha padrão é 'SYS') e a linha de comando do IRIS através de:
kubectl exec -it iris-podName-xxxx -- iris session iris
Simule a Falha
Agora pode começar a bagunçar as coisas. Mas, antes de fazê-lo, tente adicionar alguns dados na base de dados para se certificar que estarão lá quando o IRIS voltar a ficar disponível.
kubectl exec -it iris-6d8896d584-8lzn5 -- iris session iris
USER>set ^k8stest($i(^k8stest))=$zdt($h)_" running on "_$system.INetInfo.LocalHostName()
USER>zw ^k8stest
^k8stest=1
^k8stest(1)="01/14/2021 14:13:19 running on iris-6d8896d584-8lzn5"
Nossa "engenharia do caos" inicia aqui:
# Parar o IRIS - Contêiner será reiniciado automaticamente
kubectl exec -it iris-6d8896d584-8lzn5 -- iris stop iris quietly
# Deletar o pod - O Pod será recriado
kubectl delete pod iris-6d8896d584-8lzn5
# "Drenar à força" o nó que está servindo o pod IRIS - O Pod seria recriado em outro nó
kubectl drain aks-agentpool-29845772-vmss000001 --delete-local-data --ignore-daemonsets --force
# Deletar o nó - O Pod seria recriado em outro nó
# bem... você não pode realmente fazê-lo com o kubectl. Localize a instância ou a VM e a destrua.
# se você possuir acesso à maquina - desligue a força ou desconecte o cabo de rede. Estou falando sério!
Requisitos de Alta Disponibilidade
Estamos construindo um sistema que possa tolerar uma falha das seguintes:
Instância IRIS dentro de um contêiner/VM. Falha a nível do IRIS.
Falha no Pod/Contêiner.
Indisponibilidade temporária do nó de cluster individual. Um bom exemplo seria quando uma Zona de Disponibilidade fica temporariamente fora do ar.
Falha permanente do nó de cluster individual ou disco.
Basicamente os cenários que executamos na seção ˜Simule a falha".
Se alguma destas falhas ocorrer, o sistema deverá se recuperar sem que necessite de nenhum envolvimento humano e sem que ocorra nenhuma perda de dados.. Tecnicamente existem limites do que a persistência de dados garante. O IRIS por si só provê com base no Ciclo do Journal e no uso de transações em uma aplicação: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=GCDI_journal#GCDI_journal_writecycle De qualquer forma, estamos falando de menos de dois segundos de objetivo de ponto de recuperação (RPO).
Outros componentes do sistema (Serviço de APIs Kubernetes, base de dados etcd, serviço de Balanceamento de Carga, DNS e outros) estão fora do escopo e são gerenciados tipicamente pelo Serviço Gerenciado Kubernetes como o Azure AKS ou AWS EKS, então assumimos que eles já estão em alta disponibilidade.
Outra forma de se ver – somos responsáveis por lidar com falhas individuais de componentes e armazenamento, assumindo que o resto é tratado pelo provedor de infraestrutura/nuvem.
Arquitetura
Quando se trata de alta disponibilidade para o InterSystems IRIS, a recomendação tradicional é a utilização de Espelhamento. Utilizando o Espelhamento você terá duas instâncias ligadas do IRIS replicando de forma síncrona os dados. Cada nó mantém uma cópia completa da base de dados e, se o nó principal falhar, os usuários reconectam no nó Backup. Essencialmente, com a abordagem de uso do Espelhamento, o IRIS é responsável pela redundância tanto de computação quanto de armazenamento.
Com os servidores de espelhamento implantados em zonas de disponibilidade distintas o espelhamento provê a redundância necessária tanto para falhas de computação quanto para falhas de armazenamento e permite o excelente objetivo de tempo de recuperação (RTO - tempo necessário para que um sistema se recupere após uma falha) de apenas poucos segundos. Você pode encontrar o modelo de implantação para o IRIS Espelhado na Nuvem AWS aqui: https://community.intersystems.com/post/intersystems-iris-deployment%C2%A0guide-aws%C2%A0using-cloudformation-template
O lado menos bonito do espelhamento é a complexidade de configurá-lo, realizando procedimentos de backups e restore e lidando com a falta de replicação para configurações de segurança e arquivos locais que não os de bases de dados.
Orquestradores de contêineres como o Kubernetes (espere, estamos em 2021… exitem outros?!) proveem uma redundância computacional através da implantação de objetos, automaticamente reiniciando o Pod/Contêiner IRIS no caso de falha. É por isso que você vê apenas um nó IRIS executando no diagrama de arquitetura Kubernetes. Ao invés de manter um segundo nó de IRIS executando nós terceirizamos a disponibilidade de computação para o Kubernetes. O Kubernetes se certificará que o pod IRIS pode ser recriado no caso de falha do pod original por qualquer motivo.
Figura 2 Cenario de Failover
Tudo bem até agora… Se o nó do IRIS falhar, o Kubernetes apenas cria um novo. Dependendo do seu cluster, ele leva algo entre 10 a 90 segundos para trazer o IRIS de volta ao ar após uma falha de computação. É um passo atrás comparado com apenas alguns segundos no espelhamento mas, se é algo que você pode tolerar no caso de um evento indesejado, a recompensa é a grande redução da complexidade. Sem configuração de espelhamento e nem configurações de segurança e replicações de arquivos com que se preocupar.
Sinceramente, se você se logar em um contêiner, executando o IRIS no Kubernetes, você nem mesmo perceberá que está executando em um ambiente de alta disponibilidade. Tudo parece e se comporta como uma implantação de uma instância individual de IRIS.
Espere, e quanto ao armazenamento? Estamos lidando com um banco de dados … Seja qual for o cenário de falha que possamos imaginar, nosso sistema deverá cuidar da persistência dos dados também. O Espelhamento depende da computação, local no nó IRIS. Se o nó morre ou fica temporariamente indisponível – o armazenamento para o nó também o fica. É por isso que na configuração de espelhamento o IRIS cuida da replicação das bases de dados no nível do IRIS.
Precisamos de um armazenamento que possa não só preservar o estado da base de dados na reinicialização do contêiner mas também possa prover redundância em casos como a queda do nó ou de um segmento inteiro da rede (Zona de Disponibilidade). A apenas alguns anos atrás não existia uma resposta fácil para isso.Como você pode supor a partir do diagrama acima – temos uma baita resposta agora. É chamada de armazenamento distribuído de contêineres.
O armazenamento distribuído abstrai os volumes de hospedeiros subjacentes e os apresenta como um armazenamento conjunto disponível para todos os nós do cluster k8s. Nós utilizamos o Longhorn https://longhorn.io neste artigo; é grátis, de código aberto e bem fácil de instalar. Mas, você também pode verificar outros como o OpenEBS, Portworx e StorageOS que devem disponibilizar as mesmas funcionalidades. Rook Ceph é outro projeto de incubação CNCF a se considerar. No outro lado do espectro existem soluções de armazenamento de nível empresarial como o NetApp, PureStorage e outros.
Guia Passo a Passo
Na seção TL;DR nós instalamos tudo de uma vez. O Apêndice B lhe guiará através da instalação passo a passo e dos procedimentos de validação.
Armazenamento Kubernetes
Vamos voltar um pouco por um segundo e falar sobre contêineres e armazenamento em geral e como o IRIS se encaixa no cenário.
Por padrão todos os dados dentro do contêiner são efêmeros. Quando o contêiner morre, o dado desaparece. No Docker, você pode utilizar o conceito de volumes. Essencialmente isto permite que você exponha o diretório de seu SO hospedeiro para o contêiner.
docker run --detach
--publish 52773:52773
--volume /data/dur:/dur
--env ISC_DATA_DIRECTORY=/dur/iconfig
--name iris21 --init intersystems/iris:2020.3.0.221.0
No exemplo acima estamos iniciando o contêiner IRIS e fazendo com que o diretório local do hospedeiro ‘/data/dur’ fique acessível no ponto de montagem ‘/dur’ do contêiner. Desta forma, se o contêiner estiver armazenando qualquer coisa neste diretório, ela será preservada e disponível para utilização quando o próximo contêiner iniciar.
Do lado IRIS das coisas, podemos instruir o IRIS para armazenar todos os dados que devem sobreviver ao reinicio do contêiner em um diretório determinado especificando ISC_DATA_DIRECTORY. Durable SYS é o nome da funcionalidade do IRIS que você pode precisar dar uma olhada na documentação https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=ADOCK#ADOCK_iris_durable_running
No Kubernetes a sintaxe é diferente mas os conceitos são os mesmos.
Aqui está a Implantação Kubernetes básica para IRIS.
apiVersion: apps/v1
kind: Deployment
metadata:
name: iris
spec:
selector:
matchLabels:
app: iris
strategy:
type: Recreate
replicas: 1
template:
metadata:
labels:
app: iris
spec:
containers:
- image: store/intersystems/iris-community:2020.4.0.524.0
name: iris
env:
- name: ISC_DATA_DIRECTORY
value: /external/iris
ports:
- containerPort: 52773
name: smp-http
volumeMounts:
- name: iris-external-sys
mountPath: /external
volumes:
- name: iris-external-sys
persistentVolumeClaim:
claimName: iris-pvc
Na especificação de implantação acima, a parte ‘volumes’ lista os volumes de armazenamento. Eles podem estar disponíveis fora do contêiner através do Requisição de Volume Persistente (PersistentVolumeClaim) como ‘iris-pvc’. O 'volumeMounts' expõe este volume dentro do contêiner. ‘iris-external-sys’ é o identificador que amarra a montagem do volume ao volume específico. Na verdade, podemos ter múltiplos volumes e este nome é utilizado apenas para distinguir um de outro. Você pode chamá-lo de ‘steve’ se quiser.
A variável de ambiente ISC_DATA_DIRECTORY, já familiar, instrui o IRIS a utilizar um ponto de montagem específico para armazenar todos os dados que precisam sobreviver ao reinício do contêiner.
Agora vamos dar uma olhada na Requisição de Volume Persistente iris-pvc.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: iris-pvc
spec:
storageClassName: longhorn
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Bastante direto. Requisitando 10 gigabytes, montado como Read/Write em apenas um nó, utilizando a classe de armazenamento de ‘longhorn’.
Aquela storageClassName: longhorn é de fato crítica aqui.
Vamos olhar quais classes de armazenamento estão disponíveis no meu cluster AKS:
kubectl get StorageClass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
azurefile kubernetes.io/azure-file Delete Immediate true 10d
azurefile-premium kubernetes.io/azure-file Delete Immediate true 10d
default (default) kubernetes.io/azure-disk Delete Immediate true 10d
longhorn driver.longhorn.io Delete Immediate true 10d
managed-premium kubernetes.io/azure-disk Delete Immediate true 10d
Existem poucas classes de armazenamento do Azure, instaladas por padrão e uma do Longhorn que instalamos como parte de nosso primeiro comando:
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
Se você comentar #storageClassName: longhorn na definição da Requisição de Volume Persistente, será utilizada a classe de armazenamento que estiver marcada como “default” que é o disco regular do Azure.
Para ilustrar porquê precisamos de armazenamento distribuído vamos repetir o experimento da “engenharia do caos” que descrevemos no início deste artigo sem o armazenamento longhorn. Os dois primeiros cenários (parar o IRIS e deletar o Pod) deveriam completar com sucesso e os sistemas deveriam se recuperar ao estado operacional. Ao tentar tanto drenar ou destruir o nó deveria deixar o sistema em estado de falha.
#drenar o nó a força
kubectl drain aks-agentpool-71521505-vmss000001 --delete-local-data --ignore-daemonsets
kubectl describe pods
...
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 57s (x9 over 2m41s) default-scheduler 0/3 nodes are available: 1 node(s) were unschedulable, 2 node(s) had volume node affinity conflict.
Essencialmente, o Kubernetes tentará reiniciar o pod IRIS pod no cluster mas, o nó onde ele iniciou originalmente não está disponível e os outros dois nós apresentam “conflito de afinidade de nó de volume”. Com este tipo de armazenamento o volume só está disponível no nó onde ele foi originalmente criado, visto que ele é basicamente amarrado ao disco disponível no nó hospedeiro.
Com o longhorn como classe de armazenamento, tanto o experimento “drenar a força” quanto o “destruir nó” funcionam com sucesso e o pod IRIS retorna a operação brevemente. Para conseguí-lo o Longhorn assume controle dos armazenamentos disponíveis dos 3 nós do cluster e replica os dados através deles. O Longhorn prontamente repara o armazenamento do cluster se um dos nós fica permanentemente indisponível. No nosso cenário “Destruir nó” o pod IRIS é reiniciado em outro nó rapidamente utilizando as replicas nos dois outros volumes remanescentes. Então, o AKS provisiona um novo nó para substituir o nó perdido e assim que ele está pronto, o Longhorn entra em ação e reconstrói os dados necessários no novo nó. Tudo é automático, sem seu envolvimento.
Figura 3 Longhorn reconstruindo a réplica do volume no nó substituído
Mais sobre implantação k8s
Vamos dar uma olhada em alguns outros aspectos de nossa implantação:
apiVersion: apps/v1
kind: Deployment
metadata:
name: iris
spec:
selector:
matchLabels:
app: iris
strategy:
type: Recreate
replicas: 1
template:
metadata:
labels:
app: iris
spec:
containers:
- image: store/intersystems/iris-community:2020.4.0.524.0
name: iris
env:
- name: ISC_DATA_DIRECTORY
value: /external/iris
- name: ISC_CPF_MERGE_FILE
value: /external/merge/merge.cpf
ports:
- containerPort: 52773
name: smp-http
volumeMounts:
- name: iris-external-sys
mountPath: /external
- name: cpf-merge
mountPath: /external/merge
livenessProbe:
initialDelaySeconds: 25
periodSeconds: 10
exec:
command:
- /bin/sh
- -c
- "iris qlist iris | grep running"
volumes:
- name: iris-external-sys
persistentVolumeClaim:
claimName: iris-pvc
- name: cpf-merge
configMap:
name: iris-cpf-merge
strategy: Recreate, replicas: 1 informa ao Kubernetes que em algum momento ele deve manter uma e exatamente uma instância de do pod IRIS executando. Isto é o que dá conta do nosso cenário “deletar pod”.
A seção livenessProbe garante que o IRIS está sempre ativo no contêiner e trata o cenário “IRIS está fora”. initialDelaySeconds garante algum tempo para que o IRIS inicie. Você pode querer aumentá-lo se o IRIS estiver levando um tempo considerável para iniciar em sua implementação.
CPF MERGE funcionalidade do IRIS que lhe permite modificar o conteúdo do arquivo de configuração iris.cpf durante a inicialização do contêiner. Veja https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_cpf#RACS_cpf_edit_merge para a documentação referente a ela. Neste exemplo estou utilizando o Kubernetes Config Map para gerenciar o conteúdo do arquivo mesclado: https://github.com/antonum/ha-iris-k8s/blob/main/iris-cpf-merge.yaml Aqui ajustamos os valores para os global buffers e gmheap, utilizados pela instância do IRIS, mais tudo que você pode encontrar no arquivo iris.cpf. Você pode até mesmo alterar a senha padrão do IRIS utilizando o campo `PasswordHash`no arquivo CPF Merge. Leia mais em: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=ADOCK#ADOCK_iris_images_password_auth
Além da Requisição de Volume Persistente https://github.com/antonum/ha-iris-k8s/blob/main/iris-pvc.yaml da implantação https://github.com/antonum/ha-iris-k8s/blob/main/iris-deployment.yaml e ConfigMap com o conteúdo do CPF Merge https://github.com/antonum/ha-iris-k8s/blob/main/iris-cpf-merge.yaml nossa implantação precisa de um serviço que exponha a implantação IRIS para a Internet pública: https://github.com/antonum/ha-iris-k8s/blob/main/iris-svc.yaml
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
iris-svc LoadBalancer 10.0.18.169 40.88.123.45 52773:31589/TCP 3d1h
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 10d
O IP Externo do iris-svc pode ser utilizado para acessar o portal de administração do IRIS através de http://40.88.123.45:52773/csp/sys/%25CSP.Portal.Home.zen. A senha padrão é 'SYS'.
Backup/Restore e Dimensionamento do Armazenamento
Longhorn disponibiliza uma interface web para usuários para configurar e gerenciar volumes.
Identificar o pod, executar o componente de interface para usuários (longhorn-ui) e estabelecer um encaminhamento de portas com kubectl:
kubectl -n longhorn-system get pods
# note the longhorn-ui pod id.
kubectl port-forward longhorn-ui-df95bdf85-gpnjv 9000:8000 -n longhorn-system
A interface para usuários Longhorn ficará disponível em http://localhost:9000
Figura 4 Longhorn UI
Além da alta disponibilidade, a maioria das soluções de armazenamento para contêineres disponibilizam opções convenientes para backup, snapshots e restore. Os detalhes são específicos para cada implantação mas a convenção comum é de que o backup é associado ao VolumeSnapshot. Também é assim para o Longhorn. Dependendo da sua versão do Kubernetes e do seu provedor você também pode precisar instalar o volume snapshotter https://github.com/kubernetes-csi/external-snapshotter
`iris-volume-snapshot.yaml` é um exemplo de um snapshot de volume. Antes de utilizá-lo você deve configurar backups ou para um bucket S3 ou para um volume NFS no Longhorn. https://longhorn.io/docs/1.0.1/snapshots-and-backups/backup-and-restore/set-backup-target/
# Realizar um backup consistente do volume IRIS
kubectl apply -f iris-volume-snapshot.yaml
Para o IRIS é recomendado que você execute o External Freeze antes de realizar o backup/snapshot e então o Thaw depois. Veja os detalhes aqui: https://docs.intersystems.com/irisforhealthlatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=Backup.General#ExternalFreeze
Para aumentar o tamanho do volume IRIS - ajuste a requisição de armazenamento na requisição de volume persistente (arquivo `iris-pvc.yaml`), utilizado pelo IRIS.
...
resources:
requests:
storage: 10Gi #altere este valor para o necessário
Então, aplique novamente a especificação pvc. O Longhorn não consegue aplicar esta alteração enquanto o volume está conectado ao Pod em execução. Temporariamente altere o contador de réplicas para zero na implantação para que o tamanho do volume possa ser aumentado.
Alta Disponibilidade – Visão Geral
No início deste artigo nós definimos alguns critérios para alta disponibilidade. Aqui está como conseguimos alcançá-los com esta arquitetura:
Domínio de Falha
Mitigado automaticamente por
Instância IRIS no contêiner/VM. Falha no nível do IRIS.
Sonda Deployment Liveness reinicia o contêiner no caso do IRIS estar fora
Falha de Pod/Contêiner.
Implantação recria o Pod
Indisponibilidade temporária de um nó do cluster individual. Um bom exemplo seria uma Zona de Disponibilidade fora.
Implantação recria o pod em outro nó. O Longhorn torna os dados disponíveis em outro nó.
Falha permanente de um nó de cluster individual ou disco.
Mesmo do anteiror + k8s cluster autoscaler substituindo um nó danificado por um novo. O Longhorn reconstrói os dados no novo nó.
Zumbis e outras coisas a considerar
Se você estiver familiarizado em executar o IRIS em contêineres Docker, você já deve ter utilizado a flag `--init`.
docker run --rm -p 52773:52773 --init store/intersystems/iris-community:2020.4.0.524.0
O objetivo desta flag é previnir a formação de processos "zumbis". No Kubernetes, você pode tanto utilizar ‘shareProcessNamespace: true’ (considerações de segurança se aplicam) ou em seus próprios contêineres utilizar `tini`. Exemplo de Dockerfile com tini:
FROM iris-community:2020.4.0.524.0
...
# Add Tini
USER root
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
USER irisowner
ENTRYPOINT ["/tini", "--", "/iris-main"]
Desde 2021, todas as imagens de contêineres disponibilizadas pela InterSystems incluiria tini por padrão.
Você pode posteriormente diminuir o tempo de recuperação para os cenários “Drenar a força o nó/destruir nó” ajustando poucos parâmetros:
Política de Deleção de Pods do Longhorn https://longhorn.io/docs/1.1.0/references/settings/#pod-deletion-policy-when-node-is-down e despejo baseado em taint do kubernetes: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/#taint-based-evictions
Disclaimer
Como funcionário InterSystems, Eu tenho que colocar isto aqui: O Longhorn é utilizado neste artigo como um exemplo de Armazenamento em Blocos Distribuído para Kubernetes. A InterSystems não valida e nem emite uma declaração de suporte oficial para soluções ou produtos de armazenamento individual. Você precisa testar e validar se qualquer solução específica de armazenamento atende a suas necessidades.
Solucões para armazenamento distribuído podem possuir características substancialmente distintas de performance., quando comparadas a armazenamento em nó local. Especialmente para operações de escrita, onde os dados devem ser escritos em múltiplas localidades antes de ser considerado em estado persistente. Certifique-se de testar suas cargas de trabalho e entender o comportamento específico, bem como as opções que seu driver para Interface de Armazenamento de Contêiner (CSI) oferece.
Basicamente, a InterSystems não valida e/ou endossa soluções específicas de armazenamento como o Longhorn da mesma forma que não valida marcas de HDs ou fabricantes de hardware para servidores. Eu pessoalmente achei o Longhorn fácil de se utilizar e seu time de desenvolvimento extremamente responsivo e útil na página do projeto no GitHub. https://github.com/longhorn/longhorn
Conclusão
O ecossistema Kubernetes evoluiu de forma significante nos últimos anos e, com a utilização de soluções de armazenamento em blocos distribuído, você pode agora construir uma configuração de Alta Disponibilidade que pode manter uma instância IRIS, nó de cluster e até mesmo falhas de Zonas de Disponibilidade.
Você pode terceirizar a alta disponibilidade de computação e armazenamento para componentes do Kubernetes, resultando em um sistema significativamente mais simples de se configurar e manter, comparando-se ao espelhamento tradicional do IRIS. Da mesma forma, esta configuração pode não lhe prover o mesmo RTO e nível de performance de armazenamento que uma configuração de Espelhamento.
Neste artigo criamos uma configuração IRIS de alta disponibilidade utilizando o Azure AKS como Kubernetes gerenciado e o sistema de armazenamento distribuído Longhorn. Você pode explorar múltiplas alternativas como AWS EKS, Google Kubernetes Engine para K8s gerenciados, StorageOS, Portworx e OpenEBS para armazenamento distribuído para contêiner ou mesmo soluções de armazenamento de nível empresarial como NetApp, PureStorage, Dell EMC e outras.
Apêndice A. Criando um Cluster Kubernetes na nuvem
Serviço Gerenciado Kubernetes de um dos provedores públicos de nuvem é uma forma fácil de criar um cluster k8s necessário para esta configuração.A configuração padrão do AKS da Azure é pronto para ser utilizado para a implantação descrita neste artigo.
Criar um novo cluster AKS com 3 nós. Deixe todo o resto padrão.
Figura 5 Criar um cluster AKS
Instale o kubectl em seu computador localmente: https://kubernetes.io/docs/tasks/tools/install-kubectl/
Registre seu cluster AKS com o kubectl local
Figura 6 Registre o cluster AKS com kubectl
Depois disto, você pode voltar para o início do artigoe instalar o longhorn e a implantação IRIS.
A instalação no AWS EKS é um pouco mais complicada. Você precisa se certificar que cada instância em seu grupo de nós tem o open-iscsi instalado.
sudo yum install iscsi-initiator-utils -y
Instalar o Longhorn no GKE necessita de um passo extra, descrito aqui: https://longhorn.io/docs/1.0.1/advanced-resources/os-distro-specific/csi-on-gke/
Apêndice B. Instalação Passo a Passo
Passo 1 – Cluster Kubernetes e kubectl
Você precisa um cluster k8s com 3 nós. Apêndice A descreve como conseguir um na Azure.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-agentpool-29845772-vmss000000 Ready agent 10d v1.18.10
aks-agentpool-29845772-vmss000001 Ready agent 10d v1.18.10
aks-agentpool-29845772-vmss000002 Ready agent 10d v1.18.10
Passo 2 – Instalar o Longhorn
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
Certifique-se de que todos os pods no namespace ‘longhorn-system’ estão no estado de em execução. Isso pode levar alguns minutos.
$ kubectl get pods -n longhorn-system
NAME READY STATUS RESTARTS AGE
csi-attacher-74db7cf6d9-jgdxq 1/1 Running 0 10d
csi-attacher-74db7cf6d9-l99fs 1/1 Running 1 11d
...
longhorn-manager-flljf 1/1 Running 2 11d
longhorn-manager-x76n2 1/1 Running 1 11d
longhorn-ui-df95bdf85-gpnjv 1/1 Running 0 11d
Consulteo guia de instalação do Longhornpara detalhes e solução de problemas https://longhorn.io/docs/1.1.0/deploy/install/install-with-kubectl
Passo 3 – Faça um clone do repositório GitHub
$ git clone https://github.com/antonum/ha-iris-k8s.git
$ cd ha-iris-k8s
$ ls
LICENSE iris-deployment.yaml iris-volume-snapshot.yaml
README.md iris-pvc.yaml longhorn-aws-secret.yaml
iris-cpf-merge.yaml iris-svc.yaml tldr.yaml
Passo 4 – implemente e valide os componentes um a um
o arquivo tldr.yaml contém todos os componentes necessários para a implantação em um pacote. Aqui iremos instalá-los um a um e validar a configuração de cada um deles individualmente.
# Se você aplicou o tldr.yaml previamente, apague-o.
$ kubectl delete -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml
# Criar a Requisição de Volume Persistente
$ kubectl apply -f iris-pvc.yaml
persistentvolumeclaim/iris-pvc created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
iris-pvc Bound pvc-fbfaf5cf-7a75-4073-862e-09f8fd190e49 10Gi RWO longhorn 10s
# Criar o Mapa de Configuração
$ kubectl apply -f iris-cpf-merge.yaml
$ kubectl describe cm iris-cpf-merge
Name: iris-cpf-merge
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
merge.cpf:
----
[config]
globals=0,0,800,0,0,0
gmheap=256000
Events: <none>
# criar a implantação iris
$ kubectl apply -f iris-deployment.yaml
deployment.apps/iris created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
iris-65dcfd9f97-v2rwn 0/1 ContainerCreating 0 11s
# Anote o nome do pod. Você irá utilizá-lo para conectar ao pod no próximo comando
$ kubectl exec -it iris-65dcfd9f97-v2rwn -- bash
irisowner@iris-65dcfd9f97-v2rwn:~$ iris session iris
Node: iris-65dcfd9f97-v2rwn, Instance: IRIS
USER>w $zv
IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2020.4 (Build 524U) Thu Oct 22 2020 13:04:25 EDT
# h<enter> to exit IRIS shell
# exit<enter> to exit pod
# acesse os logs do contêiner IRIS
$ kubectl logs iris-65dcfd9f97-v2rwn
...
[INFO] ...started InterSystems IRIS instance IRIS
01/18/21-23:09:11:312 (1173) 0 [Utility.Event] Private webserver started on 52773
01/18/21-23:09:11:312 (1173) 0 [Utility.Event] Processing Shadows section (this system as shadow)
01/18/21-23:09:11:321 (1173) 0 [Utility.Event] Processing Monitor section
01/18/21-23:09:11:381 (1323) 0 [Utility.Event] Starting TASKMGR
01/18/21-23:09:11:392 (1324) 0 [Utility.Event] [SYSTEM MONITOR] System Monitor started in %SYS
01/18/21-23:09:11:399 (1173) 0 [Utility.Event] Shard license: 0
01/18/21-23:09:11:778 (1162) 0 [Database.SparseDBExpansion] Expanding capacity of sparse database /external/iris/mgr/iristemp/ by 10 MB.
# crie o serviço iris
$ kubectl apply -f iris-svc.yaml
service/iris-svc created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
iris-svc LoadBalancer 10.0.214.236 20.62.241.89 52773:30128/TCP 15s
Passo 5 – Acesse o portal de administração
Finalmente – conecte-se ao portal de administração do IRIS, utilizando o IP externo do serviço: http://20.62.241.89:52773/csp/sys/%25CSP.Portal.Home.zen usuário _SYSTEM, senha SYS. Será solicitado que você altere no seu primeiro login.
Artigo
Gabriel Vellasques Tureck · Mar. 22, 2021
Olá comunidade,
Você sabia sobre OWASP e os dez principais riscos de segurança de aplicativos da Web para sua API da Web ou aplicativos da Web?
OWASP é uma fundação comunitária criada para nos ajudar a melhorar a segurança de aplicativos / APIs da web. O OWASP torna os aplicativos da web mais seguros por meio de seus projetos de software de código aberto liderados pela comunidade, centenas de capítulos em todo o mundo, dezenas de milhares de membros e hospedando conferências locais e globais.
Para resumir os principais procedimentos para tornar seu aplicativo da web / API da web mais seguro, o OWASP publicou as recomendações "Top 10 Web Application Security Risks" (fonte: https://owasp.org/www-project-top-ten/):
1- Injection.. Falhas de injeção, como injeção de SQL, NoSQL, OS e LDAP, ocorrem quando dados não confiáveis são enviados a um intérprete como parte de um comando ou consulta. Os dados hostis do invasor podem induzir o intérprete a executar comandos indesejados ou acessar dados sem a autorização adequada.2- Broken Authentication.. As funções de aplicativo relacionadas à autenticação e gerenciamento de sessão são frequentemente implementadas incorretamente, permitindo que os invasores comprometam senhas, chaves ou tokens de sessão ou explorem outras falhas de implementação para assumir as identidades de outros usuários temporária ou permanentemente.3- Sensitive Data Exposure. Muitos aplicativos da web e APIs não protegem adequadamente dados confidenciais, como finanças, saúde e PII. Os invasores podem roubar ou modificar esses dados fracamente protegidos para conduzir fraude de cartão de crédito, roubo de identidade ou outros crimes. Os dados confidenciais podem ser comprometidos sem proteção extra, como criptografia em repouso ou em trânsito, e requerem precauções especiais quando trocados com o navegador.4- XML External Entities (XXE). Muitos processadores XML mais antigos ou mal configurados avaliam referências de entidades externas em documentos XML. Entidades externas podem ser usadas para divulgar arquivos internos usando o manipulador de URI de arquivo, compartilhamentos de arquivos internos, varredura de porta interna, execução remota de código e ataques de negação de serviço.5- Broken Access Control. As restrições sobre o que os usuários autenticados têm permissão para fazer muitas vezes não são aplicadas de forma adequada. Os invasores podem explorar essas falhas para acessar funcionalidades e / ou dados não autorizados, como acessar contas de outros usuários, visualizar arquivos confidenciais, modificar dados de outros usuários, alterar direitos de acesso, etc.6- Security Misconfiguration. A configuração incorreta de segurança é o problema mais comum. Isso geralmente é o resultado de configurações padrão inseguras, configurações incompletas ou ad hoc, armazenamento em nuvem aberta, cabeçalhos HTTP configurados incorretamente e mensagens de erro detalhadas contendo informações confidenciais. Não apenas todos os sistemas operacionais, estruturas, bibliotecas e aplicativos devem ser configurados com segurança, mas também devem ser corrigidos / atualizados em tempo hábil.7- Cross-Site Scripting (XSS). As falhas de XSS ocorrem sempre que um aplicativo inclui dados não confiáveis em uma nova página da web sem validação ou escape adequado, ou atualiza uma página da web existente com dados fornecidos pelo usuário usando uma API do navegador que pode criar HTML ou JavaScript. O XSS permite que os invasores executem scripts no navegador da vítima, que podem sequestrar as sessões do usuário, desfigurar sites ou redirecionar o usuário para sites maliciosos.8- Insecure Deserialization. A desserialização insegura geralmente leva à execução remota de código. Mesmo que as falhas de desserialização não resultem na execução remota de código, elas podem ser usadas para realizar ataques, incluindo ataques de repetição, ataques de injeção e ataques de escalonamento de privilégios.9- Using Components with Known Vulnerabilities. Componentes, como bibliotecas, estruturas e outros módulos de software, são executados com os mesmos privilégios do aplicativo. Se um componente vulnerável for explorado, esse tipo de ataque pode facilitar a perda séria de dados ou o controle do servidor. Aplicativos e APIs que usam componentes com vulnerabilidades conhecidas podem minar as defesas do aplicativo e permitir vários ataques e impactos.10- Insufficient Logging & Monitoring. O registro e o monitoramento insuficientes, juntamente com a integração ausente ou ineficaz com a resposta a incidentes, permitem que os invasores ataquem ainda mais os sistemas, mantenham a persistência, se movam para mais sistemas e adulterem, extraiam ou destruam dados. A maioria dos estudos de violação mostra que o tempo para detectar uma violação é de mais de 200 dias, normalmente detectado por partes externas em vez de processos internos ou monitoramento.
Algumas técnicas podem ser usadas para implementar os dez primeiros OWASP, consulte a tabela:
Recursos de recomendação OWASP
Recursos
Injection
No SQL, use parâmetros de entrada especificados usando o “?” caractere ou variáveis de host de entrada (por exemplo,: var). Evite concatenar instruções SQL e argumentos de método de entrada.Use a API Sanitazer como https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API
Broken Authentication
Crie senhas fortes, consulte o documento da Microsoft: https://support.microsoft.com/en-us/windows/create-and-use-strong-passwords-c5cebb49-8c53-4f5e-2bc4-fe357ca048eb
Não use senhas dentro do código-fonte, criptografe-o, use serviços de cofre ou serviços SSO.
Externalize suas senhas usando parâmetros do ambiente Docker / OS
Não permita ataques de força bruta usando gateways de API como IAM: https://docs.intersystems.com/components/csp/docbook/Doc.View.cls?KEY=CIAM1.5
Sensitive Data Exposure
Exposição de dados confidenciais Evite retornar dados de cartão de crédito, dados de saúde e outros dados confidenciais em sua API, se não for possível, criptografe os canais de mensagens e o armazenamento de dados confidenciais. Minimize a coleta de dados ao mínimo possível.
XXE
XXE Use% XMLAdaptor
Broken Access Control
Use ferramentas de gerenciamento de acesso como IAM: https://docs.intersystems.com/components/csp/docbook/Doc.View.cls?KEY=CIAM1.5
Security Misconfiguration
Atualize seu software periodicamente e execute o software Pentest em seu ambiente para realizar os procedimentos apropriados.
Siga o Guia de administração de segurança da intersystems: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS
XSS
Faça a sanitização / validação de entrada, consulte dicas OWASP: https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
Insecure deserialization
Crie hash para arquivos, crie pastas mais seguras sem permissão de execução para armazenar arquivos e validar extensões de arquivos
Using Components with Known Vulnerabilities
Na pilha da InterSystems o Apache Web Server é a ferramenta mais conhecida, portanto tome cuidado ao configurá-la para a produção, seguindo: https://www.tecmint.com/apache-security-tips/. Habilite o firewall do seu sistema operacional para ubuntu executar ufw enable (consulte: https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-18-04 )
Insufficient Logging & Monitoring
Use o Log Monitor (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCM_MONITOR)
Siga-o: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ALOG
Monitore usando SAM: https://docs.intersystems.com/sam/csp/docbook/DocBook.UI.Page.cls?KEY=ASAM
Você pode usar o software WAF em alinhamento com os dez principais OWASP, consulte: https://www.g2.com/categories/api-security ou use o IAM da InterSystems. Da lista G2, gosto do 42 crunch, que permite uma conta gratuita e você indica seu arquivo OpenAPI e ele é analisado para relatar recomendações, veja em https://42crunch.com/owasp-api-security-top-10/.
Artigo
Heloisa Paiva · Abr. 22
Há treze anos, obtive dois diplomas de graduação em engenharia elétrica e matemática, e logo em seguida comecei a trabalhar em tempo integral na InterSystems, sem usar nenhuma das duas. Uma das minhas experiências acadêmicas mais memoráveis e perturbadoras foi em Estatística II. Em uma prova, eu estava resolvendo um problema de intervalo de confiança moderadamente difícil. O tempo estava acabando, então (sendo um engenheiro) escrevi a integral definida na folha de prova, digitei-a na minha calculadora gráfica, escrevi uma seta com "calculadora" sobre ela e depois escrevi o resultado. Meu professor, carinhosamente conhecido como "Dean, Dean, a Máquina de Reprovar", me chamou ao seu escritório alguns dias depois. Ele não gostou nada do meu uso da calculadora gráfica. Achei isso irracional – afinal, era Estatística II, não Cálculo II, e eu fiz a parte de Estatística II corretamente... certo? Acontece que escrever "calculadora" sobre aquela seta me rendeu zero crédito na questão e uma risada do Dean; se eu tivesse omitido, teria tirado zero na prova. Eita.
Tenho repensado bastante nesse evento recentemente. A IA Generativa faz com que minha TI-89 Titanium, coberta de adesivos de estrelas por notas máximas nas aulas de matemática do ensino médio e modificada para rodar Tetris, pareça um tijolo de plástico superfaturado. Bem, era um tijolo de plástico superfaturado há 20 anos, mas ainda é hoje também.
My trusty old TI-89 Titanium
Riscos e Regras
Com o advento de ambientes de desenvolvimento integrado (IDEs) realmente bons e habilitados por IA, existe o potencial para que a IA faça o trabalho do desenvolvedor de software de nível inicial médio... certo? Se esse desenvolvedor de software de nível inicial estiver usando Python – ou alguma outra linguagem suficientemente popular na qual os modelos foram treinados – então sim. E se você se sentir confortável com o débito técnico de montanhas de código que podem ser lixo completo – ou pior ainda, majoritariamente bom com pequenos trechos de lixo escondidos – então sim. E se você tiver algum meio místico para equipar desenvolvedores de software de nível inicial em funções de principal/arquiteto sem exigir que eles escrevam nenhum código, então sim. Isso é muitas ressalvas, e precisamos de algumas regras para mitigar esses riscos.
Nota: estes riscos são independentes das preocupações com direitos autorais/propriedade intelectual que são de mão dupla: estamos infringindo código protegido por direitos autorais no conjunto de dados de treinamento ao usar a saída da GenAI? Estamos arriscando nossa própria propriedade intelectual ao enviá-la para a nuvem? Para este artigo, assumimos que ambos estão cobertos por nossa escolha de modelo/provedor de serviços, mas estas são grandes preocupações impulsionadoras no nível corporativo.
Regra nº 1: Escopo do Trabalho
Não use GenAI para fazer algo que você mesmo não conseguiria, além ou perto dos limites da sua compreensão e capacidade atuais. Voltando à ilustração original, se você está em Estatística II, pode usar GenAI para fazer Cálculo II – mas provavelmente não Estatística II, nem Estatística I e definitivamente não Teoria da Medida. Isso significa que, se você é um estagiário ou desenvolvedor de nível inicial, não deve deixar que ela faça nenhum do seu trabalho. Usar GenAI como uma busca no Google turbinada é totalmente aceitável, e usar um autocomplete inteligente pode ser OK, apenas não deixe que ela escreva código novo e do zero para você. Se você é um desenvolvedor sênior, use-a para fazer trabalho de desenvolvedor de nível inicial em tecnologias nas quais você tem proficiência de nível sênior; pense nisso como uma delegação semelhante e revise o código como se tivesse sido escrito por um desenvolvedor de nível inicial. Minha experiência de desenvolvimento de software assistido por IA tem sido com o Windsurf, que eu gosto nesse aspecto: consegui treiná-lo um pouco, dando-lhe regras e conselhos para lembrar, seguir e (como um desenvolvedor de nível inicial) ocasionalmente aplicar no contexto errado.
Regra nº 2: Atribuição
Se a GenAI escrever uma grande parte do código para você, certifique-se de que a mensagem de commit, o próprio código e qualquer documentação legível por humanos associada sejam muito claros sobre isso. Deixe óbvio: eu não escrevi isso, um computador escreveu. Isso é um serviço para aqueles que revisam seu código: eles devem tratá-lo como escrito por um desenvolvedor de nível inicial, não por você, e como parte da revisão devem questionar se a IA está fazendo coisas além da sua profundidade técnica (o que pode estar errado – e você não saberia). É um serviço para aqueles que olham seu código no futuro e tentam determinar se é lixo. E é um serviço para aqueles que tentam treinar futuros modelos de IA em seu código, para evitar o colapso e "defeitos irreversíveis nos modelos resultantes".
Regra nº 3: Modo de Aprendizagem e Reforço
Quando você está na faculdade cursando Estatística II, há algum valor no reforço das habilidades aprendidas em Cálculo II. Para ser honesto, esqueci a maior parte de ambos agora, por falta de uso. Talvez "Dean, Dean, a Máquina de Reprovar" estivesse certo afinal. Em situações em que seu objetivo principal é aprender coisas novas ou reforçar suas habilidades existentes, novas (ou de anos atrás!), fazer todo o trabalho sozinho é a melhor maneira de seguir. A prototipagem rápida é uma exceção (embora as Regras nº 1 e nº 2 ainda se apliquem!), mas mesmo no trabalho diário regular seria prejudicial tornar-se excessivamente dependente da GenAI para realizar tarefas de "desenvolvedor de nível inicial", pessoal ou organizacionalmente. Sempre precisaremos de desenvolvedores de nível inicial, porque sempre precisaremos de desenvolvedores principais e arquitetos, e o caminho entre os dois é uma função contínua. (Aí, eu me lembro de algumas coisas de matemática!)
Isso é algo que eu adoraria ver como um recurso de IDE: alternar para o "modo de aprendizado" onde a IA observa/orienta você em vez de fazer qualquer trabalho por você. Na ausência de alguma implementação personalizada em software, você também pode optar por usar a IA dessa maneira.
Regra nº 4: Reflexão
Não se perca no turbilhão de trabalho, entregas e reuniões. Reserve um tempo para refletir. Isso é importante em geral e importante no uso da GenAI especificamente.
Essa tecnologia está tornando minha vida melhor ou pior?
Está me tornando mais inteligente ou mais burro?
Estou produzindo mais valor ou apenas mais resultado?
Essa saída inclui dívida técnica incorrida por conveniência?
Estou aprendendo de forma mais eficaz ou esquecendo como aprender?
Como as soluções da IA se comparam às que tenho em mente? Ignoro algo que a IA presta atenção? A IA sistematicamente ignora coisas que são importantes para mim?
Estou me tornando como o ChatGPT, introduzindo sem alma e aleatoriamente listas com marcadores e texto em negrito nos documentos que escrevo? (Oh não!!)
Um Pensamento Final: GenAI e Desenvolvimento Baseado em InterSystems IRIS
Em um mundo onde os desenvolvedores esperam que a GenAI seja capaz de fazer o trabalho por eles – ou pelo menos torná-lo muito mais fácil – uma de duas coisas pode acontecer:
Tecnologias e linguagens dominantes (veja-se: Python) podem se tornar superdominantes, mesmo que não sejam as melhores para a tarefa em questão. Já que a GenAI é tão boa em Python, por que usar outra coisa?
Tecnologias e linguagens de nicho (veja: ObjectScript) podem se tornar mais palatáveis para os desenvolvedores. Aprender uma nova linguagem não é tão difícil se a GenAI puder ajudá-lo a começar rapidamente e fazer as coisas direito.
Minha esperança é que, à medida que fornecedores e líderes de desenvolvimento de software perceberem os riscos que descrevi, as ferramentas tendam a apoiar o último resultado – o que é uma oportunidade para a InterSystems. Sim, as pessoas podem simplesmente usar Python Embutido para tudo, mas nosso legado tecnológico e os pontos fortes da plataforma principal também podem se tornar mais palatáveis, e o ObjectScript pode receber o amor que merece.
Artigo
Yuri Marx · jan 25, 2021
Olá comunidade,
No passado, a documentação técnica do código-fonte e dos produtos de software era gerada em chm, arquivos pdf e geradores de documentação das próprias linguagens de programação. Essa abordagem antiga tinha as seguintes limitações:1. Documentação desatualizada;2. Documentação não interativa e de difícil consulta;3. Layout sem resposta, não amigável e não aderente ao HTML;4. Incapacidade de customizar o layout da documentação;5. Incapacidade de ter documentação HTML 5 online e offline.6. Falta de suporte de Markdown.
Hoje, existem várias soluções de geração de documentação que produzem Portais de Documentação Web muito atrativos, interativos, responsivos e com opções estáticas e dinâmicas, com suporte total para HTML 5 e mais recentemente Markdown.
Veja a tabela com as opções de código aberto mais populares:
Documentação do Produto
Estrelas
MkDocsGithub Repo: https://github.com/mkdocs/mkdocs
Geração de documentação estática e dinâmica
Simples, leve e construído em Python
Caixa de pesquisa de texto completo inteligente
Vários temas e plug-ins
Suporte para Markdown e HTML
Integração com páginas Git
Extensível usando Python
Arquitetura SPA
11.4k
DocsifyGithub Repo: https://github.com/docsifyjs/docsify
Nenhum arquivo html construído estaticamente
Simples e leve (~ 21kB gzipado)
Plug-in inteligente de pesquisa de texto completo
Vários temas
API de plugin simples
Compatível com IE11
Suporte SSR
Suporte a arquivos incorporados
16.5k
DocusaurusGithub: https://github.com/facebook/docusaurus/
Desenvolvido por Markdown
Construído usando React
Pronto para traduções
Controle de versão de documento
Pesquisa de Documentos
Configuração rápida
21.4k
SlateGithub: https://github.com/slatedocs/slate
Design limpo e intuitivo
Documentação de página única
Suporte a Markdown
Destaque de sintaxe embutido
Escreva amostras de código em vários idiomas
Índice automático de rolagem suave
A documentação é editável pelos usuários através do Github
Suporte RTL
31.9k
O IRIS Publisher no Open Exchange permite que você extraia blocos de documentação XData em HTML ou Markdown e gere o site de documentação de seu aplicativo usando MkDocs. Consulte as instruções no artigo: https://community.intersystems.com/post/creating-documentation-portal-your-intersystems-iris-application.
Se você gostou, vote no meu app: https://openexchange.intersystems.com/contest/current
Ref: https://dev.to/nafis/5-free-static-documentation-generators-you-must-check-out-5ged
Artigo
Heloisa Paiva · Maio 9
Às vezes, clientes precisam de uma pequena instância IRIS para fazer algo na nuvem e desligá-la, ou precisam de centenas de contêineres (ou seja, um por usuário final ou um por interface) com pequenas cargas de trabalho. Este exercício surgiu para ver quão pequena uma instância IRIS poderia ser. Para este exercício, focamos em qual é a menor quantidade de memória que podemos configurar para uma instância IRIS. Você conhece todos os parâmetros que afetam a memória alocada pelo IRIS?
Configurações de Memória
Estes são os diferentes grupos que afetam a alocação de memória pelo IRIS e seus parâmetros correspondentes:
Global Buffers (32 MB) => O mínimo parece ser 32 MB para bancos de dados de 8k
Routine Buffers (26MB) => O mínimo teórico da documentação é de 430 buffers x 64k por buffer ≈ 26 MB e na prática é 26 MB
Journal Buffers (16 MB) => Em teoria e na prática, o mínimo para um sistema Unicode é 16 MB (8 MB para 8-bit)
Buffer descriptors (3 MB) => Parece ser 3 MB, não é possível ajustar
gmheap (16 MB) => O mínimo é de 256 páginas x 64k por página = 16 MB (configurado em bytes no CPF = 16384)
ECP (1 MB) => Existem alguns parâmetros no CPF que controlam isso. MaxServerConn e MaxServers. Mesmo com MaxServerConn=0 e MaxServers=0, o mínimo é 1 MB porque o IRIS aloca memória para essas estruturas independentemente de você usar ECP ou não. Com MaxServerConn=1 e MaxServers=2, o padrão é 5 MB.
Miscellaneous (15 MB) => Pelos meus testes, o mínimo parece ser 15 MB. Não consegui ir abaixo disso.
Com todas essas configurações definidas dessa forma, os parâmetros do CPF ficariam assim:
globals=0,0,32,0,0,0
routines=0,0,0,0,0,26
jrnbufs=16
gmheap=16384
MaxServerConn=0
MaxServers=0
Portanto, se você configurar o IRIS com esses parâmetros (eu usei o IRIS 2024.1) no arquivo CPF, pode-se esperar um total de 111 MB como a quantidade mínima de memória a ser alocada pelo IRIS na inicialização (processos de usuário à parte). Você pode ver a memória alocada pelo IRIS no arquivo messages.log:
Bem, eu sei, deve haver algum arredondamento acontecendo porque se você somar todos os números individuais, obtém 109 MB, mas no messages.log é reportado como 111 MB.
Durante este processo, também descobrimos que os contêineres IRIS não conseguiam alocar Huge Pages. Isso foi reportado e corrigido em versões subsequentes pela InterSystems.
A propósito, este artigo também pode servir como um artigo geral para aprender sobre os diferentes parâmetros de memória que se pode ajustar dentro do IRIS para obter melhor desempenho.
Espero que gostem!
Artigo
Yuri Marx · Mar. 23, 2022
A partir da versão 2021.2 do InterSystems IRIS é possível desenvolver serviços de backend, de integração e procedures de bancos de dados utilizando Python. A grande vantagem desta possibilidade é a redução na curva de aprendizado e a utilização de programadores especialistas na linguagem de programação que mais cresce no mundo. O propósito deste artigo é de demonstrar que os projetos criados em InterSystems IRIS podem ser desenvolvidos com Python, ou mesmo com Python e ObjectScript (linguagem de programação proprietária da InterSystems) juntos, para atender a quaisquer requisitos e necessidades do negócio. O desafio com este artigo é processar imagens de forma geral usando Python, uma vez que o ObjectScript não conta com funcionalidades nativas para isto.
Para ilustrar na prática este casamento perfeito do Python e do InterSystems IRIS, foi criada uma aplicação de exemplo, o iris-image-editor. Esta aplicação possui as seguintes funcionalidades:
Criação de thumbnail (miniatura) na imagem;
Criação de marca d´agua na imagem;
Criação de filtros na imagem, como filtro blur, dentre outros.
Esta aplicação utiliza a biblioteca da comunidade Python Pillow (https://pillow.readthedocs.io/en/stable/). Ela permite realizar desde de operações básicas (leitura, gravação, corte, ampliação, redução, etc.), até as mais avançadas em imagens (aplicação de filtros em geral).
Para obter e executar o iris-image-editor, siga os passos abaixo:
Clone o repositório para um diretório local à sua escolha
$ git clone https://github.com/yurimarx/iris-image-editor.git
Abra o terminal do docker no diretório escolhido e execute:
$ docker-compose build
Execute o container do IRIS:
$ docker-compose up -d
Para criar thumbnails (miniaturas): Vá no seu Postman (ou cliente REST similar) e configure a requisição como na imagem a seguir, envie e veja a resposta:
Method: POST
URL: http://localhost:52773/iris-image-editor/thumbnail
Body: form-data
Key: file (o nome do campo file deve ser file) e o type File
Value: arquivo do seu computador
Para fazer a marca d´agua: Vá no seu Postman (ou outro cliente REST similar) e configure a requisição como na figura, envie e veja o resultado:
Method: POST
URL: http://localhost:52773/iris-image-editor/watermark
Body: form-data
Key: file (o nome do campo file deve ser file) e o tipo File
Value: arquivo do seu computador
Key: watermark (texto a ser escrito dentro da imagem) e o type é text
Value: I'm a cat (ou outro valor que você queira)
Para fazer filtros: Vá no seu Postman (ou outro cliente REST) e configurar a requisição como na imagem, envie e veja a resposta:
Method: POST
URL: http://localhost:52773/iris-image-editor/filter
Body: form-data
Key: file (o nome do campo deve ser file) e o type File
Value: arquivo do seu computador
Key: filter (deve ser este nome filter) e o tipo text
Value: BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, SMOOTH, SMOOTH_MORE ou SHARPEN
Como o Python foi utilizado dentro do IRIS neste exemplo:
A primeira ação a ser tomada é ter o Python instalado junto ao IRIS, no nosso exemplo, utilizamos Docker para instalar e executar o IRIS. Sendo assim, incluimos a instalação do Python e da biblioteca Pillow no arquivo Dockerfile, veja:
Trecho de código do Dockerfile responsável por instalar o Python, o PIP e o Pillow
# install libraries required by Pillow to process images
RUN apt-get -y update \
&& apt-get -y install apt-utils \
&& apt-get install -y build-essential unzip pkg-config wget \
&& apt-get install -y python3-pip
# use pip3 (the python zpm) to install Pillow dependencies
RUN pip3 install --upgrade pip setuptools wheel
RUN pip3 install --target /usr/irissys/mgr/python Pillow
O apt-get install da biblioteca do Linux python3-pip, realiza a instalação do Python e do gerenciador de pacotes/bibliotecas do Python (como o ZPM do ObjectScript ou o Maven do Java), o PyPI (pip3). A partir do pip3 é possível instalar qualquer biblioteca conhecida do mundo Python, inclusive o Pillow.
O comando pip3 install --target /usr/irissys/mgr/python Pillow instala o Pillow e suas dependências dentro do diretório no qual o IRIS procura bibliotecas Python para utilizar (quando no ambiente Linux).
Uma vez que temos o Python e as bibliotecas necessárias instaladas, podemos agora criar nossos métodos de classe em linguagem Python, ao invés de utilizar ObjectScript. Neste exemplo criamos três métodos, todos na classe dc.imageeditor.ImageEditorService, detalhados abaixo:
Método de Classe em Python para gerar thumbnails para imagens
ClassMethod ProcessThumbnail(imageName) [ Language = python ]
{
#Import required Image library
from PIL import Image
# set folder to receive the image to be processed
input_path = "/opt/irisbuild/input/" + imageName
# set folder to stores the results (image processed)
output_path = "/opt/irisbuild/output/" + imageName
# open the original image
image = Image.open(input_path)
# reduce image size
image.thumbnail((90,90))
# save the new image
image.save(output_path)
}
O primeiro passo é declarar Language = python na declaração do método, assim o IRIS irá utilizar Python, ao invés de ObjectScript, que a linguagem padrão. A seguir, basta escrever todo o conteúdo do método em Python, como seria feito dentro de um arquivo .py.
Este método importa o Pillow com o from PIL import Image. Em seguida são definidos os diretórios de origem da imagem e de destino da imagem processada. O Image.open, obtêm a imagem a ser processada. O image.thumbnail reduz o tamanho da imagem para as dimensões 90x90. O image.save grava o processamento no diretório de destino.
Método de Classe em Python que escreve um texto como marca d'agua da imagem
ClassMethod ProcessWartermarker(imageName, watermark) [ Language = python ]
{
#Import required Image library
from PIL import Image, ImageDraw, ImageFont
# set folder to receive the image to be processed
input_path = "/opt/irisbuild/input/" + imageName
# set folder to stores the results (image processed)
output_path = "/opt/irisbuild/output/" + imageName
# open the original image
im = Image.open(input_path)
# extract image dimensions
width, height = im.size
# get drawer
draw = ImageDraw.Draw(im)
# get text writer
font = ImageFont.truetype("DejaVuSans.ttf", 32)
textwidth, textheight = draw.textsize(watermark, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
# draw watermark in the bottom right corner
draw.text((x, y), watermark, font=font)
# save the new image
im.save(output_path)
}
O Image.open() abre a imagem de origem. O im.size obtêm as dimensões da imagem. O ImageDraw.Draw obtem uma referência chamada draw que irá permitir a operação de escrita na imagem. O ImageFont.truetype() escolhe a fonte e tamanho do texto a ser escrito. É calculada a posição x,y do texto na image e o draw.text escreve o texto na fonte escolhida. A imagem final é gravada no diretório de destino.
Método de Classe em Python para aplicar filtros avançados na imagem
ClassMethod ProcessFilter(imageName, filter) [ Language = python ]
{
#Import required Image library
from PIL import Image, ImageFilter
#Import all the enhancement filter from pillow
from PIL.ImageFilter import (
BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE,
EMBOSS, FIND_EDGES, SMOOTH, SMOOTH_MORE, SHARPEN
)
# set folder to receive the image to be processed
input_path = "/opt/irisbuild/input/" + imageName
# set folder to stores the results (image processed)
output_path = "/opt/irisbuild/output/" + imageName
# open the original image
im = Image.open(input_path)
if filter == "BLUR":
imFiltered = im.filter(BLUR)
elif filter == "CONTOUR":
imFiltered = im.filter(CONTOUR)
elif filter == "DETAIL":
imFiltered = im.filter(DETAIL)
elif filter == "EDGE_ENHANCE":
imFiltered = im.filter(CONTOUR)
elif filter == "EDGE_ENHANCE_MORE":
imFiltered = im.filter(EDGE_ENHANCE_MORE)
elif filter == "EMBOSS":
imFiltered = im.filter(EMBOSS)
elif filter == "FIND_EDGES":
imFiltered = im.filter(FIND_EDGES)
elif filter == "SHARPEN":
imFiltered = im.filter(SHARPEN)
elif filter == "SMOOTH":
imFiltered = im.filter(SMOOTH)
elif filter == "SMOOTH_MORE":
imFiltered = im.filter(SMOOTH_MORE)
else:
imFiltered = im
# save the new image
imFiltered.save(output_path)
}
O Image.open() abre a imagem de origem. O im.filter(NOME DO FILTRO) aplica o filtro e retorna uma refêrencia para a imagem processada. A imagem com o filtro aplicado é gravada no diretório de destino com imFiltered.save().
A interação entre o ObjectScript e o Python
O exemplo também demonstra como é fácil para o ObjectScript realizar uma chamada para um método escrito em Python. É da mesma forma como seria chamado qualquer outro método. Veja este trecho de exemplo:
ClassMethod DoThumbnail(Image As %String) As %Status
{
Do ..ProcessThumbnail(Image)
Quit $$$OK
}
O método de classe escrito em Python está na mesma classe deste método de classe em ObjectScript, então basta utilizar .. seguido do nome do método e seus argumentos de chamada. O IRIS internamente realiza as conversões de tipos na ida e na volta para os argumentos.
Se o método estiver em outra classe, basta utilizar Do ##class(pacote.NomeClasse).NomeMetodoPython(<argumentos>).
Expondo as funcionalidades como API REST
O IRIS possui extrema facilidade em expor métodos de classe como API REST, basta ter uma classe que herde de %CSP.REST. Nesta aplicação de exemplo isto foi feito a partir da classe dc.imageeditor.ImageEditorRESTApp. Veja:
Classe responsável por expor as funcionalidades como API REST
Class dc.imageeditor.ImageEditorRESTApp Extends %CSP.REST
{
Parameter CHARSET = "utf-8";
Parameter CONVERTINPUTSTREAM = 1;
Parameter CONTENTTYPE = "application/json";
Parameter Version = "1.0.0";
Parameter HandleCorsRequest = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<!-- Server Info -->
<Route Url="/" Method="GET" Call="GetInfo" Cors="true"/>
<!-- Swagger specs -->
<Route Url="/_spec" Method="GET" Call="SwaggerSpec" />
<!-- do a thumbnail -->
<Route Url="/thumbnail" Method="POST" Call="DoThumbnail" />
<!-- do a watermark -->
<Route Url="/watermark" Method="POST" Call="DoWatermark" />
<!-- do a image filter -->
<Route Url="/filter" Method="POST" Call="DoFilter" />
</Routes>
}
/// Do thumbnail
ClassMethod DoThumbnail() As %Status
{
Set tSC = $$$OK
try {
// get the file from the multipart request
Set source = %request.GetMimeData("file")
// save the file to the input folder, to be processed with imageai
Set destination=##class(%Stream.FileBinary).%New()
Set destination.Filename="/opt/irisbuild/input/"_source.FileName
set tSC=destination.CopyFrom(source) //reader open the file
set result=destination.%Save()
//call embedded python classmethod to thumbnail the image
Do ##class(dc.imageeditor.ImageEditorService).DoThumbnail(source.FileName)
If ($FIND(source.FileName, "jpg") > 0) || ($FIND(source.FileName, "jpeg") > 0) {
Set %response.ContentType = "image/jpeg"
} ElseIf ($FIND(source.FileName, "png") > 0) {
Set %response.ContentType = "image/png"
} Else {
Set %response.ContentType = "application/octet-stream"
}
Do %response.SetHeader("Content-Disposition","attachment;filename="""_source.FileName_"""")
Set %response.NoCharSetConvert=1
Set %response.Headers("Access-Control-Allow-Origin")="*"
Set stream=##class(%Stream.FileBinary).%New()
Set sc=stream.LinkToFile("/opt/irisbuild/output/"_source.FileName)
Do stream.OutputToDevice()
Set tSC=$$$OK
//returns error message to the user
} catch e {
Set tSC=e.AsStatus()
Set pOutput = tSC
}
Quit tSC
}
/// Do Watermark
ClassMethod DoWatermark() As %Status
{
Set tSC = $$$OK
try {
// get the file from the multipart request
Set source = %request.GetMimeData("file")
Set watermark = $Get(%request.Data(("watermark"),1))
// save the file to the input folder, to be processed with image editor
Set destination=##class(%Stream.FileBinary).%New()
Set destination.Filename="/opt/irisbuild/input/"_source.FileName
set tSC=destination.CopyFrom(source) //reader open the file
set result=destination.%Save()
//call embedded python classmethod to thumbnail the image
Do ##class(dc.imageeditor.ImageEditorService).DoWatermark(source.FileName, watermark)
If ($FIND(source.FileName, "jpg") > 0) || ($FIND(source.FileName, "jpeg") > 0) {
Set %response.ContentType = "image/jpeg"
} ElseIf ($FIND(source.FileName, "png") > 0) {
Set %response.ContentType = "image/png"
} Else {
Set %response.ContentType = "application/octet-stream"
}
Do %response.SetHeader("Content-Disposition","attachment;filename="""_source.FileName_"""")
Set %response.NoCharSetConvert=1
Set %response.Headers("Access-Control-Allow-Origin")="*"
Set stream=##class(%Stream.FileBinary).%New()
Set sc=stream.LinkToFile("/opt/irisbuild/output/"_source.FileName)
Do stream.OutputToDevice()
Set tSC=$$$OK
//returns error message to the user
} catch e {
Set tSC=e.AsStatus()
Set pOutput = tSC
}
Quit tSC
}
/// Do filter
ClassMethod DoFilter() As %Status
{
Set tSC = $$$OK
try {
// get the file from the multipart request
Set source = %request.GetMimeData("file")
Set filter = $Get(%request.Data(("filter"),1))
// save the file to the input folder, to be processed with image editor
Set destination=##class(%Stream.FileBinary).%New()
Set destination.Filename="/opt/irisbuild/input/"_source.FileName
set tSC=destination.CopyFrom(source) //reader open the file
set result=destination.%Save()
//call embedded python classmethod to thumbnail the image
Do ##class(dc.imageeditor.ImageEditorService).DoFilter(source.FileName, filter)
If ($FIND(source.FileName, "jpg") > 0) || ($FIND(source.FileName, "jpeg") > 0) {
Set %response.ContentType = "image/jpeg"
} ElseIf ($FIND(source.FileName, "png") > 0) {
Set %response.ContentType = "image/png"
} Else {
Set %response.ContentType = "application/octet-stream"
}
Do %response.SetHeader("Content-Disposition","attachment;filename="""_source.FileName_"""")
Set %response.NoCharSetConvert=1
Set %response.Headers("Access-Control-Allow-Origin")="*"
Set stream=##class(%Stream.FileBinary).%New()
Set sc=stream.LinkToFile("/opt/irisbuild/output/"_source.FileName)
Do stream.OutputToDevice()
Set tSC=$$$OK
//returns error message to the user
} catch e {
Set tSC=e.AsStatus()
Set pOutput = tSC
}
Quit tSC
}
/// General information
ClassMethod GetInfo() As %Status
{
SET version = ..#Version
SET fmt=##class(%SYS.NLS.Format).%New("ptbw")
SET info = {
"Service": "Image Editor API",
"version": (version),
"Developer": "Yuri Gomes",
"Status": "Ok",
"Date": ($ZDATETIME($HOROLOG))
}
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Write info.%ToJSON()
Quit $$$OK
}
ClassMethod SwaggerSpec() As %Status
{
Set tSC = ##class(%REST.API).GetWebRESTApplication($NAMESPACE, %request.Application, .swagger)
Do swagger.info.%Remove("x-ISC_Namespace")
Set swagger.basePath = "/iris-tts"
Set swagger.info.title = "TTS Service API"
Set swagger.info.version = "1.0"
Set swagger.host = "localhost:52773"
Return ..%ProcessResult($$$OK, swagger)
}
ClassMethod %ProcessResult(pStatus As %Status = {$$$OK}, pResult As %DynamicObject = "") As %Status [ Internal ]
{
#dim %response As %CSP.Response
SET tSC = $$$OK
IF $$$ISERR(pStatus) {
SET %response.Status = 500
SET tSC = ..StatusToJSON(pStatus, .tJSON)
IF $isobject(tJSON) {
SET pResult = tJSON
} ELSE {
SET pResult = { "errors": [ { "error": "Unknown error parsing status code" } ] }
}
}
ELSEIF pStatus=1 {
IF '$isobject(pResult){
SET pResult = {
}
}
}
ELSE {
SET %response.Status = pStatus
SET error = $PIECE(pStatus, " ", 2, *)
SET pResult = {
"error": (error)
}
}
IF pResult.%Extends("%Library.DynamicAbstractObject") {
WRITE pResult.%ToJSON()
}
ELSEIF pResult.%Extends("%JSON.Adaptor") {
DO pResult.%JSONExport()
}
ELSEIF pResult.%Extends("%Stream.Object") {
DO pResult.OutputToDevice()
}
QUIT tSC
}
}
A classe extende de %CSP.REST e possui uma seção <Routes> responsável por criar as rotas (URL) HTTP para utilizar as funcionalidades. Na tag <Route> é definida a propriedade Url como o caminho HTTP utilizado para chamar o método de classe indicado na propriedade Call. O verbo HTTP é escolhido na propriedade Method.
A chamada %request.GetMimeData() é responsável por receber os valores de input da requisição. O %response.ContentType define o tipo de retorno da requisição e a chamada %Stream.FileBinary.%New() permite obter uma referência para um arquivo, basta requisitar LinkToFile(), indicando o caminho para o arquivo. Por último é executado o OutputToDevice() para escrever o conteúdo do arquivo na resposta HTTP.
Outras formas de utilizar o Python e IRIS juntos (fonte: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AEPYTHON)
A partir do terminal do IRIS, é possível acionar o ambiente de execução do Python (shell):
USER>do ##class(%SYS.Python).Shell()
A partir de um programa Python é possível chamar Classes e Métodos ObjectScript
# import the iris module and show the classes in this namespace
import iris
print('\nInterSystems IRIS classes in this namespace:')
status = iris.cls('%SYSTEM.OBJ').ShowClasses()
print(status)
Ou
>>> import iris
>>> status = iris.cls('User.EmbeddedPython').Test()
Fibonacci series:
0 1 1 2 3 5 8
InterSystems IRIS classes in this namespace:
User.Company
User.EmbeddedPython
User.Person
>>> print(status)
1
Em funções e Stored Procedures SQL:
CREATE FUNCTION tzconvert(dt DATETIME, tzfrom VARCHAR, tzto VARCHAR)
RETURNS DATETIME
LANGUAGE PYTHON
{
from datetime import datetime
from dateutil import parser, tz
d = parser.parse(dt)
if (tzfrom is not None):
tzf = tz.gettz(tzfrom)
d = d.replace(tzinfo = tzf)
return d.astimezone(tz.gettz(tzto)).strftime("%Y-%m-%d %H:%M:%S")
}
A partir da classe utilitária %SYS.Python
Class Demo.PDF
{
ClassMethod CreateSamplePDF(fileloc As %String) As %Status
{
set canvaslib = ##class(%SYS.Python).Import("reportlab.pdfgen.canvas")
set canvas = canvaslib.Canvas(fileloc)
do canvas.drawImage("C:\Sample\isc.png", 150, 600)
do canvas.drawImage("C:\Sample\python.png", 150, 200)
do canvas.setFont("Helvetica-Bold", 24)
do canvas.drawString(25, 450, "InterSystems IRIS & Python. Perfect Together.")
do canvas.save()
}
}
Usando o Python Buitin Functions
set builtins = ##class(%SYS.Python).Import("builtins")
USER>do builtins.print("hello world!")
hello world!
Saiba mais:
Documentação oficial: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AEPYTHON
Concurso passado sobre Python com excelentes exemplo de uso: https://openexchange.intersystems.com/contest/21
Outros recursos:
Learning Path Writing Python Application with InterSystems
Embedded Python Documentation
Native API for Python Documentation
PEX Documentation
Artigo
Heloisa Paiva · Set. 22, 2022
Aqui você vai encontrar um programa simples que usa Python em um ambiente de desenvolvimento IRIS e outro programa simples que sua ObjectScript em um ambiente de desenvolvimento Python. Além disso, gostaria de compartilhar alguns dos problemas que tive enquanto aprendia a implementar esses códigos.
Python em ambiente IRIS
Digamos, por exemplo, que você está desenvolvendo no IRIS e tem um problema que acha mais fácil ou mais eficiente de se resolver com Python.
Você pode simplesmente trocar o ambiente: crie seu método como qualquer outro, mas ao final do nome e de suas especificações, adicione [ Language = python ]:
Você pode usar todo tipo de argumento no método e, para acessá-los, a lógica é exatamente a mesma que em COS:
Temos um argumento %String, chamado Arg, e um argumento OtherArg que vem de uma classe customizada. Essa classe pode ter propriedades como Title e Author. Você pode acessá-las pelo nome:
Esse método retorna um output desta forma:
Para acessar métodos de classe, é basicamente o mesmo. Se há um método na classe Demo.Books.PD.Books (do OtherArg), chamado "CreateString", que concatena o título e o autor dados para algo do tipo "Title: <Title>; Author: <Author>", então, adicionar isso no final do nosso método python:
acrescentará ao output:
Para acessar o método, só é necessário OtherArg.CreateString(), mas eu escolhi passar os mesmos valores em OtherArg para o método com CreateString(OtherArg) para que os outputs ficassem parecidos e, consequentemente, o código ficasse mais simples.
ObjectScript em ambiente Python
Também pode haver situações em que você está desenvolvendo em ambiente Python, mas deseja as ferramentas do ObjectScript para ajudar.
Primeiramente, é importante checar alguns itens desta lista para que possa acessar seus arquivos COS de um ambiente python de todas as maneiras (não vou necessariamente usar todas aqui nos exemplos):
Você tem os pré-requisitos? Veja aqui
Você pode usar um servidor externo de Python? Veja aqui
Você tem todos os drivers necessários? Faça o download aqui ou Descubra mais aqui
Você sempre pode retornar a esses links ou dar uma olhada na lista de erros que eu encontrei enquanto estava criando meus primeiros programas de Python com COS, se achar útil.
Então, vamos começar a codar!
Primeiro, vamos ter que adaptar algumas coisas do COS para o Python. Por sorte, a InterSystems já deixou isso pronto e só precisamos digitar "import iris" no começo do código para acessar tudo!
No próximo passo, vamos criar uma conexão ao namespace desejado, usando um caminho com o host, port e namespace (host:port/namespace) e providenciando o usuário e senha:
Observe como no final criamos um OBJETO IRIS, para utilizarmos essa conexão para ter acesso a tudo o que necessitamos no namespace.
Finalmente, pode "codar" tudo o que quiser:
Você pode acessar métodos com o irispy.classMethodValue() providenciando o nome da classe, do método e seus argumentos, manipular objetos com .set() (para propriedades) e muitas outras possibilidades, enquanto trata tudo com Python da forma preferida.
Para mais informações sobre funções providenciadas pelo iris e como usá-las, visite o link Introduction to the Native SDK for Python
Nesse exemplo, na linha 16 eu instanciei uma classe persistente para, nas linhas seguintes, dar os valores "Lord of The Rings" e "Tolkien" às propriedades Title e Author.
Na linha 20, chamei um método de outra classe que salva o objeto a uma tabela e retorna um status se funcionou corretamente. Finalmente, imprimo o status na linha 23.
Mix!
Em um ambiente de ObjectScript, você pode precisar das Libraries conhecidas do Python, ou então de seus próprios arquivos customizados com funções ou rotinas.
Você pode usar o comando "import" com Numpy, SciPy e qualquer outras que desejar (considerando que as tenha corretamente instaladas: veja aqui como fazer isso)
Há muitas maneiras para acessar seus arquivos locais e é fácil achar tutoriais para isso na internet, considerando que Python é uma linguagem bem popular.
Para mim, a mais fácil de trabalhar é a seguinte:
Aqui, importei tudo do arquivo testesql.py, localizado em C:/python e imprimi os resultados da função select()
Extras - problemas que encontrei
SHELLS: Ao usar os Shells, lembre-se que o Windows PowerShell funciona como um sistema UNIX-based (já que é baseado em .NET) e o Command Prompt é o que vai funcionar corretamente com os exemplos de Windows na documentação oficial da InterSystems. Isso pode soar básico para alguns programadores mais experientes, mas se não estiver prestando atenção nesse detalhe pode perder um bom tempo tentando entender, então achei interessante pontuar isso aqui.
USUÁRIOS E PRIVILÉGIOS: O usuário utilizado para programar com ObjectScript de um ambiente Python precisa ter privilégios aos recursos do Namespace. Lembre-se que se você não selecionar nenhum, o padrão é UnkownUser, e se não selecionar namespaces, o padrão é USER. Então, no acesso mais simples, siga: Portal de Administração > Administração do Sistema > Segurança > Usuários > UnknownUser > Roles, selecione %DB_USER e salve.
SOCORRO, EU NEM SEI O QUE ESTÁ ACONTECENDO: Para checar mais informações sobre os erros que você está encotrando, siga: Portal de Administração > Explorer do Sistema > SQL e digite "SELECT * FROM %SYS.Audit ORDER BY UTCTimeStamp Desc" para retornar as audits mais recentes. (Lembre-se de checar o namespace para consultar essa query). Aí você irá encontrar as causas de erros como IRIS_ACCESSDENIED() e muito mais, que você pode encontrar até fora do ambiente IRIS.
ERRO AO COMPILAR PYTHON: Evite dar nomes a métodos como try() ou outras funções prontas do Python. O compilador não entenderá a diferença do nome do Método para a função.
Muito obrigada pela leitura e por favor, sinta-se à vontade para compartilhar sugestões, comentários ou dúvidas, ou simplesmente o que você estiver desenvolvendo!
Artigo
Danusa Calixto · Out. 27, 2022
Digamos que eu esteja desenvolvendo uma aplicação web que usa o IRIS como back-end. Estou trabalhando nela com acesso não autenticado. Está chegando a hora em que eu gostaria de implantar para os usuários, mas preciso adicionar a autenticação primeiro. Em vez de usar a autenticação padrão de senha do IRIS, quero que os usuários façam login com o Login Único (single sign-on - SSO, na sigla em inglês) da minha organização ou outro provedor de identidade popular, como Google ou GitHub. Li que o OpenID Connect é um padrão de autenticação comum e compatível com o IRIS. Qual é a maneira mais simples de colocar isso em funcionamento? ### Exemplo 1: uma app CSP simples A documentação [aqui](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GOAUTH_client) apresenta uma opção bastante fácil para usar uma aplicação CSP como um cliente OpenID Connect. Estas são as etapas: 1. Configure o servidor OAuth 2.0 e a configuração do cliente no IRIS. Veja mais informações na seção "Caché configuration" do[excelente artigo do Daniel Kutac](https://community.intersystems.com/post/intersystems-iris-open-authorization-framework-oauth-20-implementation-part-1). 2. Copie a rotina OAUTH2.ZAUTHENTICATE do [repositório de amostras no GitHub](https://github.com/intersystems/Samples-Security/blob/master/rtn/OAUTH2.ZAUTHENTICATE.mac) para o namespace %SYS e renomeie como ZAUTHENTICATE. 3. Ative a autenticação delegada no sistema inteiro. 4. Crie uma página de login personalizada que seja uma extensão de %OAuth2.Login e substitua o método DefineParameters para especificar o nome do aplicativo OAuth 2.0 e os escopos:
Class MyOAuth2.Login Extends %OAuth2.Login
{
ClassMethod DefineParameters(Output application As %String, Output scope As %String, Output responseMode As %String)
{
Set application="my application name"
Set scope="openid profile email"
Set responseMode=..#RESPONSEMODE
Quit
}
}
5. Ative a aplicação web para a autenticação delegada e defina MyOAuth2.Login.cls como a página de login personalizada. 6. Um truque final: para a página de login personalizada funcionar, o usuário CSPSystem no IRIS precisa ter o acesso específico de LEITURA à base de dados em que está MyOAuth2.Login.cls. Neste momento, o login deve "simplesmente funcionar": a visita a uma página CSP nesta aplicação web redirecionará para a página de login no provedor de identidade. Depois de fazer o login, o usuário terá uma sessão CSP autenticada. O $username será igual ao identificador de sujeito do SSO/Google/GitHub/etc., então posso usar a autorização integrada do IRIS para determinar a que dar acesso. ### Exemplo 2: o problema com REST E se a aplicação web estiver usando um manipulador REST? O processo acima não funciona. Se uma aplicação web estiver habilitada para REST, não é possível definir uma página de login personalizada. Descobri que são necessárias mais algumas etapas para contornar isso. 7. Crie uma aplicação web separada sem REST habilitado. O caminho nessa app precisa começar com o caminho do app REST. Por exemplo, se o nome da app REST for "/csp/api", você poderá nomear essa nova app como "/csp/api/login". Ative a autenticação delegada e defina a página MyOAuth2.Login.cls como a página de login personalizada. 8. Defina o Caminho do Cookie da Sessão nessa nova app para o mesmo da app REST: por exemplo, "/csp/api". Assim, ambas as aplicações compartilharão uma sessão CSP. 9. Adicione uma página CSP a essa nova app para servir como uma "página inicial". Um usuário precisa primeiro acessar essa página para estabelecer sua sessão. Veja este exemplo que redireciona para um endpoint na API REST após o login:
Class App.Home Extends %CSP.Page
{
ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{
&html<<script type="text/javascript"> window.location="/csp/api/test" </script>>
return $$$OK
}
}
10. Certifique-se de que a classe do manipulador REST tenha o parâmetro UseSession substituído por true.
Class API.REST Extends %CSP.REST
{
Parameter UseSession As BOOLEAN = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/test" Method="GET" Call="Test" Cors="true"/>
</Routes>
}
ClassMethod Test() As %Status
{
write { "username": ($username) }.%ToJSON()
return $$$OK
}
}
Nesse ponto, o login na app REST também deverá "simplesmente funcionar". O usuário visitará a página inicial, será redirecionado para o login SSO e, finalmente, retornará a app REST, onde tem uma sessão CSP autenticada. Até onde sei, essa é a maneira mais fácil de adicionar o OpenID Connect a um app IRIS REST. Outra opção é usar a amostra "REST.ZAUTHENTICATE" do repositório de amostras de segurança. Com isso, espera-se que o front-end anexe um bearer token OAuth 2.0 a cada solicitação. No entanto, não há uma forma definida para o front-end obter esse token de acesso. Você terá que implementar esse fluxo OAuth por conta própria em JavaScript (ou usar uma biblioteca como [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc).) Você também precisará se certificar de que o app JavaScript e o back-end IRIS estejam de acordo em todos os itens de configuração, como o endpoint do emissor do servidor de autorização, o ID do cliente OAuth 2.0 etc. Descobri que essa não é uma tarefa simples. Tenho curiosidade para saber se mais alguém está usando o OpenID Connect para autenticar uma app IRIS. Há uma maneira ainda mais simples? Ou vale a pena usar a abordagem mais complicada com bearer tokens? Comente aqui embaixo.
Anúncio
Rochael Ribeiro · Jun. 6, 2022
Bem vindos aos Lançamentos de Maio de 2022 da Comunidade de Desenvolvedores!
Disponibilizamos algumas melhorias para aprimorar sua experiência recentemente na Comunidade InterSystems:
🆕 Rastreamento aprimorado dos eventos atuais
🆕 Postagem agendada
🆕 Formatação de código aprimorada
🆕 Criação de tabelas mais rápida
🆕 Experiência de resposta enriquecida
🆕 Alterado a prévia de design de postagens
Vamos dar uma olhada mais próxima em todos os detalhes.
AO VIVO AGORA para Eventos
Para tornar os eventos atuais mais fáceis e convenientes de se localizar adicionamos uma nova seção AO VIVO AGORA no canto superior esquerdo da página.
E se você clicar nele, será redirecionado para a página do evento, onde você pode se juntar ao evento (se for online, claro)
Para tirar vantagem do uso desta funcionalidade, seus anúncios de eventos devem atender as seguintes condições:
Crie seu evento normalmente [adicione a tag Eventos -> campos especiais aparecerão automaticamente]
Verifique se seu evento possui horários de início e fim específicos (eventos de dia todo não são tratados nesta seção)
Para permitir que pessoas se juntem ao seu evento online diretamente, adicione a opção Link direto para participar
É isso, pronto para publicar!
Agende sua postagem
Você pediu e nós fizemos! Agora você pode agendar sua postagem para que seja publicada em um horário específico.
Para agendar sua postagem para mais tarde, você precisa apenas clicar na seta para baixo, próxima ao botão Publicar, e escolher Agendar postagem.
Depois, você poderá escolher a data e horário de quando sua postagem será publicada.
Escolhido o horário, clique no botão Agendar postagem. Sua postagem será publicada no horário escolhido - é isso!
Formatação de Código
Para compartilhar seu código com outros, adicionamos um editor embutido para quando ocorrer a inserção de códigos.
In it, you can select the appropriate programming language and tab size.
Assim, o destaque da sintaxe ocorre imediatamente ao se escrever o texto e a linguagem de programação é exibida no canto superior esquerdo.
Como resultado, na sua postagem, você terá um bonito código formatado, na linguagem de programação selecionada.
Criação de Tabelas
Para facilitar a formatação no momento da criação de tabelas, adicionamos uma função para rapidamente criar uma tabela, através da seleção do número desejado de células.
Se você clicar no botão Mais em baixo, será aberta a funcionalidade padrão de criação de tabelas.
Respostas e assinaturas
Para visualizar todas as informações sobre a discussão rapidamente, adicionamos o número de respostas no topo do editor e também um ícone para assinar uma discussão e assim, postar uma nova resposta.
Design da Prévia da Postagem
Para tornar a parte de baixo da postagem mais balanceada, rearranjamos e alteramos os ícones.
Aproveitem as atualizações!
Enviem-nos suas requisições por melhorias e notificações de problemas no GitHub da Comunidade de Desenvolvedores. Ou poste suas sugestões nos comentários desta postagem.
Nos vemos em breve com mais mudanças interessantes!
Artigo
Danusa Calixto · Nov. 4, 2022
O ZPM foi projetado para trabalhar com aplicativos e módulos para a plataforma de dados IRIS da InterSystems. Ele consiste em dois componentes, o ZPN Client que é uma CLI para gerenciar módulos, e o The Registry que é um banco de dados de módulos e meta-informações. Podemos usar o ZPM para pesquisar, instalar, atualizar, remover e publicar módulos. Com o ZPM, você pode instalar classes ObjectScript, aplicativos Frontend, produções de interoperabilidade, soluções IRIS BI, conjuntos de dados IRIS ou quaisquer arquivos como rodas Python incorporadas.
Hoje este livro de receitas passará por 3 seções:
Instalar ZPM
Gerar Módulo
Encontre, instale, publique módulos dentro do Registro
1. Instale o ZPM
Baixe a versão mais recente do ZPM (este deve ser um único arquivo XML) Link para Download
Importe o XML que você baixou para o IRIS e ele só pode ser implantado no terminal IRIS, abra o IRIS e insira
write $SYSTEM.OBJ.Load("C:\zpm.xml", "c")
Observação "C:\zpm.xml" é o caminho do arquivo XML baixado, esta etapa pode demorar um pouco.
Após terminar a instalação, basta digitar zpm, pressionar enter, você verá que está no shell zpm
2. Gerar Módulo
Antes de começarmos a gerar o módulo, precisamos preparar uma pasta que tenha um ou mais arquivos prontos para carregar, por isso criei uma pasta na unidade C chamada zpm.
Execute o comando generate C:/zpm
Depois de especificar todo o necessário, seu primeiro módulo foi gerado com sucesso, você também verá
Observação:
a versão do módulo está usando versionamento semântico
pasta de origem do módulo é a pasta que tem todo o arquivo de classe
zpm também oferece uma opção para adicionar aplicativos web e dependência, neste exemplo vou deixar em branco
Agora, abra o explorador de arquivos, você verá um arquivo chamado "module.xml" como você pode verificar na captura de tela abaixo
Digite command load C:\ZPM\ você verá que seu módulo foi recarregado, validado, compilado e ativado
3. Encontre, instale, publique módulos dentro do Registro
Encontre pacotes disponíveis no Registro atual: zpm:USER>search
Instalar o pacote do registro atual como exemplo permite instalar um módulo chamado zpmshow no registro público: zpm:USER>install zpmshow (o comando é install "moduleName")
Publicar módulo depois de carregado: zpm:USER>publish myFirstZPMDemo
Você pode usar zpm:USER>search para verificar a publicação, no meu caso você pode ver que "myfirstzpmdemo 0.1.0" está no Registro atual.
Nota: Se ocorrer um erro ao publicar um módulo que diz: "ERRO! Publicando módulo, algo deu errado", certifique-se de que o status do Registro atual esteja habilitado e disponível.
Você pode usar zpm:USER>repo -list, para verificar o status do registro atual.
Video disponível: Clique aqui
Artigo
Edilson Eberle Carvalho · Jun. 8, 2023
Incrível esse legado! Esse é um dos motivos principais do sucesso da Intersystems.
Fiz um teste simples essa semana e não é que deu certo? Qual linguagem tem essa capacidade? Qual banco de dados pode-se ter Globais e Tabelas SQL? Isso sem falar na velocidade! Será covardia o BachMark entre MS-SQL e Caché. Qual o sistema que vc pode instalar em seu notebook e apresentar resultados como se estivesse em um Servidor?
Apresentei para meus alunos, e eles ficaram fascinados com facilidade de programação, criação imediata de variáveis, globais, linha de código, etc. Como um aluno me disse: "O bagulho é loko"....rs
O programa abaixo valida CNPJ e CPF, naquela época era CGC rs....
CGC(%CGC) ;PROGRAMA DE VALIDAÇÃO CGC/CPF
; ENTRADA ==> %CGC (CGC/CPF NUMERICO)
; SAIDA ==> VVALUE (CGC/CPF C/PONTUACAO) // PP9 (1-INVALIDO, 0-S/ERRO)
N (VVALUE,PP9,%CGC) Q:%CGC="" %CGC
S %G="" F %I=1:1:$L(%CGC) S:$E(%CGC,%I)?1N %G=%G_$E(%CGC,%I)
S %CGC=%G I $L(%CGC)<14 G CPF
S VVALUE=$E(%CGC,1,2)_"."_$E(%CGC,3,5)_"."_$E(%CGC,6,8)_"/"_$E(%CGC,9,12)_"-"_$E(%CGC,13,14),%CT=$L(%CGC),%N=$E(%CGC,1,%CT-2),%DG=$E(%CGC,%CT-1,%CT),%DI="",PP9=0 D CGC1 S %N=%N_%DC,%CT=%CT+1 D CGC1 S:%DG'=%DI PP9=1
K %N,%D,%DG,%CGC,%CT,%S,%P,%DC,%CTN,%I Q VVALUE
CGC1 S %S=0,%P=2,%CTN=$L(%N) F %I=%CTN:-1:1 S %D=$E(%N,%I),%S=%P*%D+%S,%P=%P+1 S:%P>9 %P=2
S %DC=%S#11*-1+11 S:%DC>9 %DC=0 S %DI=%DI_%DC Q
CPF S VVALUE=$E(%CGC,1,3)_"."_$E(%CGC,4,6)_"."_$E(%CGC,7,9)_"-"_$E(%CGC,10,11),%CT=$L(%CGC),%N=$E(%CGC,1,%CT-2),%DG=$E(%CGC,%CT-1,%CT),%DI="",PP9=0 D CGC2 S %N=%N_%DC,%CT=%CT+1 D CGC2 S:%DG'=%DI PP9=1
K %N,%D,%DG,%CGC,%CT,%S,%P,%DC,%CTN,%I Q VVALUE
CGC2 S %S=0,%P=2,%CTN=$L(%N) F %I=%CTN:-1:1 S %D=$E(%N,%I),%S=%P*%D+%S,%P=%P+1
S %DC=%S#11*-1+11 S:%DC>9 %DC=0 S %DI=%DI_%DC Q
Artigo
Heloisa Paiva · Abr. 18

Sou um grande fã de ficção científica, mas embora eu esteja totalmente a bordo da nave Star Wars (desculpas aos meus colegas Trekkies!), sempre apreciei os episódios clássicos de Star Trek da minha infância.
A tripulação diversificada da USS Enterprise, cada um dominando suas funções únicas, é uma metáfora perfeita para entender os agentes de IA e seu poder em projetos como o Facilis.
Então, vamos embarcar em uma missão intergaláctica, utilizando a IA como a tripulação da nossa nave e ***audaciosamente ir*** audaciosamente ir ***homem jamais esteve***!
Esse conceito de trabalho em equipe é uma analogia maravilhosa para ilustrar como os agentes de IA funcionam e como os usamos em nosso projeto DC-Facilis. Então, vamos mergulhar e assumir o papel de um capitão de nave estelar, liderando uma tripulação de IA em territórios inexplorados!
## Bem-vindo ao CrewAI!
Para gerenciar nossa tripulação de IA, usamos uma estrutura fantástica chamada [CrewAI](https://www.crewai.com/).É enxuta, extremamente rápida e opera como uma plataforma Python multiagente. Uma das razões pelas quais a amamos, além do fato de ter sido criada por outro brasileiro, é sua incrível flexibilidade e design baseado em funções.
```python
from crewai import Agent, Task, Crew
```

### Conheça os Planejadores
No Facilis, nossos agentes de IA são divididos em dois grupos. Vamos começar com o primeiro, que gosto de chamar de "Os Planejadores".
### O Agente de Extração
O papel principal do Facilis é receber uma descrição em linguagem natural de um serviço REST e criar automaticamente toda a interoperabilidade necessária. Portanto, nosso primeiro membro da tripulação é o Agente de Extração. Este agente tem a tarefa de "extrair" as especificações da API a partir da descrição fornecida pelo usuário.
Aqui está o que o Agente de Extração procura:
- Host (obrigatório)
- Endpoint (obrigatório)
- Params (opcional)
- Port (se disponível)
- modelo JSON (para POST/PUT/PATCH/DELETE)
- Autenticação (se aplicável)
```python
def create_extraction_agent(self) -> Agent:
return Agent(
role='Extrator de Especificações de API',
goal='Extrair especificações de API de descrições em linguagem natural',
backstory=dedent("""
Você é especializado em interpretar descrições em linguagem natural
e extrair especificações de API estruturadas.
"""),
allow_delegation=True,
llm=self.llm
)
def extract_api_specs(self, descriptions: List[str]) -> Task:
return Task(
description=dedent(f"""
Extraia as especificações de API das seguintes descrições:
{json.dumps(descriptions, indent=2)}
Para cada descrição, extraia:
- Host (obrigatório)
- Endpoint (obrigatório)
- Params (opcional)
- Port (se disponível)
- modelo JSON (para POST/PUT/PATCH/DELETE)
- Autenticação (se aplicável)
Marque quaisquer campos obrigatórios ausentes como 'missing'.
Retorne os resultados em formato JSON como um array de especificações.
"""),
expected_output="""Um array JSON contendo as especificações de API extraídas com todos os campos obrigatórios e opcionais""",
agent=self.extraction_agent
)
```
### O Agente de Validação
Próximo na fila, o Agente de Validação! Sua missão é garantir que as especificações de API coletadas pelo Agente de Extração estejam corretas e consistentes. Ele verifica:
1. Formato de host válido
2. Endpoint começando com '/'
3. Métodos HTTP válidos (GET, POST, PUT, DELETE, PATCH)
4. Número de porta válido (se fornecido)
5. Presença de modelo JSON para métodos aplicáveis.
```python
def create_validation_agent(self) -> Agent:
return Agent(
role='API Validator',
goal='Validar especificações de API quanto à correção e consistência.',
backstory=dedent("""
Você é um especialista em validação de API, garantindo que todas as especificações
atendam aos padrões e formatos necessários.
"""),
allow_delegation=False,
llm=self.llm
)
def validate_api_spec(self, extracted_data: Dict) -> Task:
return Task(
description=dedent(f"""
Valide a seguinte especificação de API:
{json.dumps(extracted_data, indent=2)}
Verifique:
1. Formato de host válido
2. Endpoint começando com '/'
3. Métodos HTTP válidos (GET, POST, PUT, DELETE, PATCH)
4. Número de porta válido (se fornecido)
5. Presença de modelo JSON para métodos aplicáveis.
Retorne os resultados da validação em formato JSON.
"""),
expected_output="""Um objeto JSON contendo os resultados da validação com quaisquer erros ou confirmação de validade""",
agent=self.validation_agent
)
```
### O Agente de Interação
Avançando, conhecemos o Agente de Interação, nosso Especialista em Interação com o Usuário. Seu papel é obter quaisquer campos de especificação de API ausentes que foram marcados pelo Agente de Extração e validá-los com base nas descobertas do Agente de Validação. Eles interagem diretamente com os usuários para preencher quaisquer lacunas.
### O Agente de Produção
Precisamos de duas informações cruciais para criar a interoperabilidade necessária: namespace e nome da produção. O Agente de Produção interage com os usuários para coletar essas informações, de forma muito semelhante ao Agente de Interação.
### O Agente de Transformação de Documentação
Assim que as especificações estiverem prontas, é hora de convertê-las em documentação OpenAPI. O Agente de Transformação de Documentação, um especialista em OpenAPI, cuida disso.
```python
def create_transformation_agent(self) -> Agent:
return Agent(
role='Especialista em Transformação OpenAPI',
goal='Converter especificações de API em documentação OpenAPI',
backstory=dedent("""
Você é um especialista em especificações e documentação OpenAPI.
Seu papel é transformar detalhes de API validados em documentação
OpenAPI 3.0 precisa e abrangente.
"""),
allow_delegation=False,
llm=self.llm
)
def transform_to_openapi(self, validated_endpoints: List[Dict], production_info: Dict) -> Task:
return Task(
description=dedent(f"""
Transforme as seguintes especificações de API validadas em documentação OpenAPI 3.0:
Informações de Produção:
{json.dumps(production_info, indent=2)}
Endpoints Validados:
{json.dumps(validated_endpoints, indent=2)}
Requisitos:
1. Gerar especificação OpenAPI 3.0 completa
2. Incluir schemas de requisição/resposta apropriados
3. Documentar todos os parâmetros e corpos de requisição
4. Incluir autenticação se especificado
5. Garantir formatação de caminho apropriada
Retorne a especificação OpenAPI nos formatos JSON e YAML.
"""),
expected_output="""Um objeto JSON contendo a especificação OpenAPI 3.0 completa com todos os endpoints e schemas.""",
agent=self.transformation_agent
)
```
### The Review Agent
Após a transformação, a documentação OpenAPI passa por uma revisão meticulosa para garantir conformidade e qualidade. O Agente de Revisão segue esta lista de verificação:
1.Conformidade OpenAPI 3.0
- Especificação de versão correta
- Elementos raiz obrigatórios
- Validação da estrutura do schema
2. Completude
- Todos os endpoints documentados
- Parâmetros totalmente especificados
- Schemas de requisição/resposta definidos
- Esquemas de segurança configurados corretamente
3. Verificações de Qualidade
- Convenções de nomenclatura consistentes
- Descrições claras
- Uso adequado de tipos de dados
- Códigos de resposta significativos
4. Melhores Práticas
- Uso adequado de tags
- Nomenclatura de parâmetros consistente
- Definições de segurança apropriadas
Finalmente, se tudo parecer bom, o Agente de Revisão reporta um objeto JSON saudável com a seguinte estrutura:
```json
{
"is_valid": boolean,
"approved_spec": object (a especificação OpenAPI revisada e possivelmente corrigida),
"issues": [array de strings descrevendo quaisquer problemas encontrados],
"recommendations": [array de sugestões de melhoria]
}
```
```python
def create_reviewer_agent(self) -> Agent:
return Agent(
role='Revisor de Documentação OpenAPI',
goal='Garantir a conformidade e qualidade da documentação OpenAPI',
backstory=dedent("""
Você é a autoridade final em qualidade e conformidade de documentação OpenAPI.
Com vasta experiência em especificações OpenAPI 3.0, você revisa meticulosamente
a documentação para precisão, completude e adesão aos padrões.
"""),
allow_delegation=True,
llm=self.llm
)
def review_openapi_spec(self, openapi_spec: Dict) -> Task:
return Task(
description=dedent(f"""
Revise a seguinte especificação OpenAPI para conformidade e qualidade:
{json.dumps(openapi_spec, indent=2)}
Lista de Verificação da Revisão::
1. Conformidade OpenAPI 3.0
- Verificar a especificação de versão correta
- Verificar os elementos raiz obrigatórios
- Validar a estrutura do schema
2. Completude
- Todos os endpoints devidamente documentados
- Parâmetros totalmente especificados
- Schemas de requisição/resposta definidos
- Esquemas de segurança configurados corretamente
3. Quality Checks
- Consistent naming conventions
- Clear descriptions
- Proper use of data types
- Meaningful response codes
4. Best Practices
- Proper tag usage
- Consistent parameter naming
- Appropriate security definitions
Você deve retornar um objeto JSON com a seguinte estrutura:
{{
"is_valid": boolean,
"approved_spec": object (a especificação OpenAPI revisada e possivelmente corrigida),
"issues": [array de strings descrevendo quaisquer problemas encontrados],
"recommendations": [array de sugestões de melhoria]
}}
"""),
expected_output=""" Um objeto JSON contendo: is_valid (boolean), approved_spec (object), issues (array), e recommendations (array)""",
agent=self.reviewer_agent
)
```
### O Agente Iris
O último agente no grupo do planejador é o Agente Iris, que envia a documentação OpenAPI finalizada para o Iris.
```python
def create_iris_i14y_agent(self) -> Agent:
return Agent(
role='Especialista em Integração Iris I14y',
goal='Integrar especificações de API com o serviço Iris I14y',
backstory=dedent("""
Você é responsável por garantir uma integração suave entre o sistema de
documentação da API e o serviço Iris I14y. Você lida com a
comunicação com o Iris, valida as respostas e garante a integração
bem-sucedida das especificações da API.
"""),
allow_delegation=False,
llm=self.llm
)
def send_to_iris(self, openapi_spec: Dict, production_info: Dict, review_result: Dict) -> Task:
return Task(
description=dedent(f"""
Enviar a especificação OpenAPI aprovada para o serviço Iris I14y:
Informações de Produção:
- Nome: {production_info['production_name']}
- Namespace: {production_info['namespace']}
- É Novo: {production_info.get('create_new', False)}
Status da Revisão:
- Aprovado: {review_result['is_valid']}
Retornar o resultado da integração em formato JSON.
"""),
expected_output="""Um objeto JSON contendo o resultado da integração com o serviço Iris I14y, incluindo o status de sucesso e os detalhes da resposta.""",
agent=self.iris_i14y_agent
)
class IrisI14yService:
def __init__(self):
self.logger = logging.getLogger('facilis.IrisI14yService')
self.base_url = os.getenv("FACILIS_URL", "http://dc-facilis-iris-1:52773")
self.headers = {
"Content-Type": "application/json"
}
self.timeout = int(os.getenv("IRIS_TIMEOUT", "504")) # in milliseconds
self.max_retries = int(os.getenv("IRIS_MAX_RETRIES", "3"))
self.logger.info("IrisI14yService initialized")
async def send_to_iris_async(self, payload: Dict) -> Dict:
"""
Enviar carga útil para o endpoint de geração do Iris de forma assíncrona.
"""
self.logger.info("Enviando carga útil para o endpoint de geração do Iris.")
if isinstance(payload, str):
try:
json.loads(payload)
except json.JSONDecodeError:
raise ValueError("Invalid JSON string provided")
retry_count = 0
last_error = None
# Cria timeout para o aiohttp
timeout = aiohttp.ClientTimeout(total=self.timeout / 1000) # Converte ms para seconds
while retry_count < self.max_retries:
try:
self.logger.info(f"Attempt {retry_count + 1}/{self.max_retries}: Enviando requisição para {self.base_url}/facilis/api/generate")
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(
f"{self.base_url}/facilis/api/generate",
json=payload,
headers=self.headers
) as response:
if response.status == 200:
return await response.json()
response.raise_for_status()
except asyncio.TimeoutError as e:
retry_count += 1
last_error = e
error_msg = f"Timeout occurred (attempt {retry_count}/{self.max_retries})"
self.logger.warning(error_msg)
if retry_count < self.max_retries:
wait_time = 2 ** (retry_count - 1)
self.logger.info(f"Waiting {wait_time} seconds before retry...")
await asyncio.sleep(wait_time)
continue
except aiohttp.ClientError as e:
error_msg = f"Failed to send to Iris: {str(e)}"
self.logger.error(error_msg)
raise IrisIntegrationError(error_msg)
error_msg = f"Failed to send to Iris after {self.max_retries} attempts due to timeout"
self.logger.error(error_msg)
raise IrisIntegrationError(error_msg, last_error)
```
## Conheça os Geradores
Nosso segundo conjunto de agentes são os Geradores. Eles estão aqui para transformar as especificações OpenAPI em interoperabilidade InterSystems IRIS.
Há oito deles neste grupo.
O primeiro deles é o Agente Analisador. Ele é como o planejador, traçando a rota.
Seu trabalho é mergulhar nas especificações OpenAPI e descobrir quais componentes de Interoperabilidade IRIS são necessários.
```python
def create_analyzer_agent():
return Agent(
role="Analisador de Especificações OpenAPI",
goal="Analisar minuciosamente as especificações OpenAPI e planejar os componentes de Interoperabilidade IRIS",
backstory="""Você é um especialista em especificações OpenAPI e em Interoperabilidade InterSystems IRIS.
Seu trabalho é analisar documentos OpenAPI e criar um plano detalhado de como eles devem ser implementados como componentes de Interoperabilidade IRIS.""",
verbose=False,
allow_delegation=False,
tools=[analyze_openapi_tool],
llm=get_facilis_llm()
)
analysis_task = Task(
description="""Analisar a especificação OpenAPI e planejar os componentes de Interoperabilidade IRIS necessários.
Incluir uma lista de todos os componentes que devem estar na classe de Produção."",
agent=analyzer,
expected_output="Uma análise detalhada da especificação OpenAPI e um plano para os componentes IRIS, incluindo a lista de componentes de Produção",
input={
"openapi_spec": openApiSpec,
"production_name": "${production_name}"
}
)
```
Em seguida, os **Agentes de Business Services (BS) e Business Operations (BO)** assumem o controle.
Eles geram os Business Services e as Business Operations com base nos endpoints OpenAPI.
Eles usam uma ferramenta útil chamada MessageClassTool para gerar as classes de mensagens perfeitas, garantindo a comunicação.
```python
def create_bs_generator_agent():
return Agent(
role="Gerador de Produção e Business Service IRIS",
goal="Gerar classes de Produção e Business Service IRIS formatadas corretamente a partir de especificações OpenAPI",
backstory="""Você é um desenvolvedor InterSystems IRIS experiente, especializado em Produções de Interoperabilidade.
Sua expertise reside na criação de Business Services e Produções capazes de receber e processar requisições de entrada com base emespecificações de API."",
verbose=False,
allow_delegation=True,
tools=[generate_production_class_tool, generate_business_service_tool],
llm=get_facilis_llm()
)
def create_bo_generator_agent():
return Agent(
role="Gerador de Business Operation IRIS",
goal="Gerar classes de Business Operation IRIS formatadas corretamente a partir de especificações OpenAPI",
backstory="""Você é um desenvolvedor InterSystems IRIS experiente, especializado em Produções de Interoperabilidade.
Sua expertise reside na criação de Business Operations capazes de enviar requisições para sistemas externos com base em especificações de API.""",
verbose=False,
allow_delegation=True,
tools=[generate_business_operation_tool, generate_message_class_tool],
llm=get_facilis_llm()
)
bs_generation_task = Task(
description="Gerar classes de Business Service com base nos endpoints OpenAPI",
agent=bs_generator,
expected_output="Definições de classes de Business Service do IRIS",
context=[analysis_task]
)
bo_generation_task = Task(
description="Gerar classes de Business Operation com base nos endpoints OpenAPI",
agent=bo_generator,
expected_output="Definições de classes de Business Operation do IRIS",
context=[analysis_task]
)
class GenerateMessageClassTool(BaseTool):
name: str = "generate_message_class"
description: str = "Gerar uma classe de Mensagem IRIS"
input_schema: Type[BaseModel] = GenerateMessageClassToolInput
def _run(self, message_name: str, schema_info: Union[str, Dict[str, Any]]) -> str:
writer = IRISClassWriter()
try:
if isinstance(schema_info, str):
try:
schema_dict = json.loads(schema_info)
except json.JSONDecodeError:
return "Error: Invalid JSON format for schema info"
else:
schema_dict = schema_info
class_content = writer.write_message_class(message_name, schema_dict)
# Armazenar a classe gerada.
writer.generated_classes[f"MSG.{message_name}"] = class_content
return class_content
except Exception as e:
return f"Error generating message class: {str(e)}"
```
Depois que BS e BO fazem o que têm que fazer, é a hora do Agente de Produção brilhar!
Este agente junta tudo para criar um ambiente de produção coeso.
Depois que tudo estiver configurado, o próximo na linha é o Agente de Validação.
Este entra em cena para uma verificação final, garantindo que cada classe Iris esteja ok.
Em seguida, temos o Agente de Exportação e o Agente de Coleção. O Agente de Exportação gera os arquivos .cls, enquanto o Agente de Coleção reúne todos os nomes de arquivos.
Tudo é passado para o importador, que compila tudo no InterSystems Iris.
```python
def create_exporter_agent():
return Agent(
role="Exportador de Classes IRIS",
goal="Exportar e validar definições de classes IRIS para arquivos .cls adequados",
backstory="""Você é um especialista em implantação InterSystems IRIS. Seu trabalho é garantir que as definições de classes IRIS geradas sejam devidamente exportadas como arquivos .cls válidos que
possam ser importados diretamente para um ambiente IRIS.""",
verbose=False,
allow_delegation=False,
tools=[export_iris_classes_tool, validate_iris_classes_tool],
llm=get_facilis_llm()
)
def create_collector_agent():
return Agent(
role="Coletor de Classes IRIS",
goal="Coletar todos os arquivos de classes IRIS gerados em uma coleção JSON",
backstory="""Você é um especialista em sistema de arquivos responsável por reunir e
organizar os arquivos de classes IRIS gerados em uma coleção estruturada.""",
verbose=False,
allow_delegation=False,
tools=[CollectGeneratedFilesTool()],
llm=get_facilis_llm()
)
export_task = Task(
description="Exportar todas as classes IRIS geradas como arquivos .cls válidos",
agent=exporter,
expected_output="Arquivos .cls IRIS válidos salvos no diretório de saída",
context=[bs_generation_task, bo_generation_task],
input={
"output_dir": "/home/irisowner/dev/output/iris_classes" # Optional
}
)
collection_task = Task(
description="Coletar todos os arquivos de classes IRIS gerados em uma coleção JSON",
agent=collector,
expected_output="Coleção JSON de todos os arquivos .cls gerados",
context=[export_task, validate_task],
input={
"directory": "./output/iris_classes"
}
)
```
## Limitações e Desafios
Nosso projeto começou como um experimento empolgante, onde meus colegas mosqueteiros e eu almejávamos criar uma ferramenta totalmente automatizada usando agentes.
Foi uma jornada selvagem!
Nosso foco principal estava em integrações de API REST. É sempre uma alegria receber uma tarefa com uma especificação OpenAPI para integrar; no entanto, sistemas legados podem ser uma história completamente diferente.
Pensamos que automatizar essas tarefas poderia ser incrivelmente útil.
Mas toda aventura tem suas reviravoltas:
Um dos maiores desafios foi instruir a IA a converter OpenAPI para Interoperabilidade Iris.
Começamos com o modelo openAI GPT3.5-turbo, que em testes iniciais se mostrou difícil com depuração e prevenção de interrupções.
A mudança para o Anthropic Claude 3.7 Sonnet mostrou melhores resultados para o grupo Gerador, mas não tanto para os Planejadores...
Isso nos levou a dividir nossas configurações de ambiente, usando diferentes provedores de LLM para flexibilidade.
Usamos GPT3.5-turbo para planejamento e Claude sonnet para geração, uma ótima combinação!
Essa combinação funcionou bem, mas encontramos problemas com alucinações.
A mudança para o GT4o melhorou os resultados, mas ainda enfrentamos alucinações na criação de classes Iris e, às vezes, especificações OpenAPI desnecessárias, como o renomado exemplo Pet Store OpenAPI.
Nos divertimos muito aprendendo ao longo do caminho, e estou super animado com o futuro incrível nesta área, com inúmeras possibilidades!
Artigo
Bob Kuszewski · Maio 20, 2021
# Migrando do Java Business Host para o PEX
Com o lançamento do [PEX](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=EPEX) a partir do InterSystems IRIS 2020.1 e InterSystems IRIS for Health 2020.1, nossos clientes tem agora uma melhor forma de utilizar Java nas Produções de interoperabilidade que através da utilização do Java Business Host. O PEX (Production EXtension) disponibiliza um conjunto completo de APIs para criar componentes de interoperabilidade e está disponível tanto em Java quanto em .NET. O Java Business Host foi descontinuado e será aposentado em versões futuras.
Vantagens do PEX
* Permite que desenvolvedores criem qualquer componente da Produção de interoperabilidade em Java ou em .NET
* Estruturas de mensagens mais complexas podem ser enviadas através dos componentes
* Configurações simplificadas
* Fluxo de trabalho simplificado , sem utilização de ObjectScript.
O resto deste artigo está focado em como realizar a migração de código existente do Java Business Host para o PEX.
## Visão Geral
As classes e interfaces utilizadas pelo PEX são diferentes das do Java Business Host (JBH). Nós iremos disponibilizar uma visão geral das diferenças neste artigo mas a [documentação completa](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=EPEX_apiref) te dará maior profundidade sobre o assunto.
* [Convertendo um Business Service](#Converting-a-Business-Service-from-Java-Business-Host-to-PEX)
* [Convertendo um Business Operation](#Converting-a-Business-Operation-from-Java-Business-Host-to-PEX)
* [Configurações](#Settings)
* [Mensagens](#Messages)
* [Registrando](#Logging)
## Convertendo um Business Service do Java Business Host para o PEX
Para criar um Business Service PEX você precisará implementar a `com.intersystems.enslib.pex.BusinessService` ao invés da `com.intersystems.gateway.bh.BusinessService`.
O padrão de design utilizado pelo PEX para Business Service mudou de um onde era esperado que o serviço iniciasse o processo de produção de mensagens para o padrão onde o serviço implementa uma função que é invocada periodicamente para produzir mensagens.
No JBH, seu código seria algo como isso
```java
@Override
public boolean OnInit(Production p) throws Exception {
production = p;
if (messageThread == null) {
Messager messager = new Messager();
messageThread = new Thread(messager);
messageThread.start();
}
return true;
}
```
No PEX, você precisa apenas implementas três funções
```java
public void OnInit() throws Exception {
// Inicialização
return;
}
public Object OnProcessInput(Object messageInput) throws Exception {
// Aqui é onde você invoca a SendMessage() ou SendMessageAsync()
return null;
}
public void OnTearDown() throws Exception {
// Desligar
return;
}
```
Você também precisará alterar como as configurações são utilizadas, mensagens são entregues e onde o registro de log é feito. Falaremos mais sobre estas alterações abaixo.
## Convertendo um Business Operation do Java Business Host para PEX
Para criar um Business Operation PEX você precisará implementar a `com.intersystems.enslib.pex.BusinessOperation` ao invés da `com.intersystems.gateway.bh.BusinessOperation`.
O padrão de design para os Business Operations é estruturalmente o mesmo entre o JBH e o PEX entretanto os parâmetros para os dois principais pontos de entrada forma alterados.
### Mudanças no OnInit()
No PEX o `OnInit()` não recebe parâmetros.
### Mudanças no OnMessage()
No PEX o `OnMessage()` recebe um `Object` genérico no lugar da `String` utilizada no JBH. Isto permite que o criador da produção de interoperabilidade envie qualquer tipo de mensagem desejada.
No JBH sua aplicação ficaria mais ou menos algo assim:
```java
public boolean OnMessage(String message) throws Exception {
// Lógica de negócio aqui
return true;
}
```
No PEX o parâmetro é um Objeto Java genérico, que você deve instanciar apropriadamente, que permite que você transmita mensagens mais complexas que apenas strings. Aqui está um exemplo de como extrair uma requisição que é do tipo file stream.
```java
public Object OnMessage(Object request) throws Exception {
com.intersystems.jdbc.IRISObject streamContainer = (com.intersystems.jdbc.IRISObject)request;
com.intersystems.jdbc.IRISObject str = (com.intersystems.jdbc.IRISObject)streamContainer.get("Stream");
String originalFilename = (String)streamContainer.get("OriginalFilename");
Long contentSize = (Long)str.get("Size");
String content = (String)str.invoke("Read", contentSize);
// Lógica de negócio aqui
return null;
}
```
Você também precisará alterar como as configurações são utilizadas, mensagens são entregues e onde o registro de log é feito. Falaremos mais sobre estas alterações abaixo.
## Settings
A declaração das configurações também foi simplificada.
No JBH as configurações era declaradas através da string `SETTINGS` e recuperada através de código, algo como isso:
```java
String setting = production.GetSetting("Min");
if (!setting.isEmpty()) {
min = Integer.parseInt(setting);
}
```
No PEX as configurações são apenas campos de membros públicos, desta forma elas são automaticamente carregadas quando a classe é instanciada.
```java
public int Min = 0;
```
Qualquer campo de membro público está disponível para ser atribuído em sua produção de interoperabilidade desde que o campo do membro seja um tipo base Java (String, int, etc.).
## Mensagens
O envio de Mensagens no PEX é mais poderoso. No JBH mensagens são enviadas como strings. No PEX as mensagens são enviadas como objetos - tanto IRISObject, para objetos definidos em ObjectScript, ou uma subclasse subclass of `com.intersystems.enslib.pex.Message`, para classes definidas em Java.
No JBH seu código seria algo assim
```java
production.SendRequest(value.toString());
```
No PEX seria algo como isso
```java
MyExampleMessageClass req = new MyExampleMessageClass("mensagem a ser enviada");
SendRequestAsync(Target, req);
```
## Logging
As funções para registro de Logs são todas similares, apenas nomeadas de forma diferente.
No PEX você registraria uma mensagem informativa através da `LOGINFO()`
```java
LOGINFO("mensagem recebida");
```
## Gateway de Objetos
O Java Business Host precisava de seu próprio gateway. Com o PEX você pode utilizar um Java gateway apenas para toda sua necessiade de Java. Ou então usar vários, você escolhe. Aqui você encontra uma boa [introdução ao gateway](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=EJVG_intro).
## Conclusão e Feedback
Se você ainda não experimentou utilizar o PEX, o que falta ? O PEX disponibiliza a habilidade de resolver uma quantidade muito mais ampla de problemas de negócio com menos codificação, além de permitir que agora tudo também seja feito em .NET.
Se você tiver dúvidas ou problemas migrando suas aplicações de JBH para PEX, entre em contato conosco pelo WRC.
Artigo
Luana Machado · Jun. 12, 2023
Exemplo de uso do banco de dados FHIR InterSystems IRIS for Health para efetuar modelagem ML através do InterSystems IRIS IntegratedML
Descrição
IntegratedML é uma ótima funcionalidade para treino/teste e implantação de modelos ML. FHIR é um padrão poderoso para a interoperabilidade de informações da saúde. Esse projeto visa mostrar como utilizar as ferramentas IRIS/IRIS for Health. Por exemplo, as transformações DTL para preparar dados do FHIR para aplicação de modelos ML dentro do IntegratedML. Aqui estão algumas potenciais aplicações das ideias apresentadas nesse projeto:
Reutilização e extensão de transformações DTL dentro de outras bases de dados FHIR para modelos ML personalizados
Utilização das transformações DTL para normalizar mensagens FHIR e publicar modelos ML como serviços
Criação de um referencial de modelos e regras de transformação para usar em qualquer conjunto de dados FHIR
Instalação
Clone o repositório em um diretório local:
$ git clone https://github.com/jrpereirajr/fhir-integratedml-example.git
Abra o terminal nesse repositório e execute:
$ cd fhir-integratedml-example
$ docker-compose up -d
Se quiser obter um log do que aconteceu durante a instalação, utilize o seguinte comando :
$ docker-compose up -d > build-log.txt 2>&1
Inicialização de um terminal IRIS
Para inicializar um terminal IRIS, siga esses passos:
Em um terminal powershell/cmd, execute:
docker exec -it fhir-integratedml-example_iris_1 bash
Dentro do shell do linux , crie uma sessão IRIS:
irissession iris
Demonstração
Para demonstrar o conceito do projeto, foram configurados dois modelos:
Um modelo de predição de não-comparecimento à uma consulta;
Um modelo de predição de insuficiência cardíaca.
Primeiro, conjuntos de dados de treinamento foram usados para gerar recursos FHIR sintéticos. Esses conjuntos de dados continham informações sobre pacientes, patologias, observações, consultas e visitas enviadas aos pacientes, representadas por diversos recursos FHIR. Essa etapa emula um banco de dados FHIR real, no qual as previsões de não-comparecimento e insuficiência cardíaca podem ser aplicadas.
Quando o banco de dados FHIR estiver pronto para uso, os dados devem ser transformados, combinando recursos FHIR que são pertinentes ao problema, em tabelas únicas. Essa combinação de FHIR é obtida por transformações DTL, como NoShowDTL e HeartFailureDTL :
Como as transformações DTL podem ser exportadas/importadas, é possível compartilhar os modelos ML aplicados aos dados FHIR. Essas transformações podem igualmente ser entendidas por outras equipes, se necessário.
Depois de aplicar as transformações DTL, os recursos FHIR são mapeados em linhas simples, criando também tabelas que podem ser utilizadas para formar modelos ML para as previsões de não-comparecimento e insuficiência cardíaca.
Para treinar e testar os modelos usando o IntegratedML, utilize as seguintes instruções SQL. Eles são executadas durante a instalação, mas você pode executá-los novamente e experimentar o IntegratedML por conta própria.
Modelo de não-comparecimento
-- criar um conjunto de dados de treinamento
CREATE OR REPLACE VIEW PackageSample.NoShowMLRowTraining AS SELECT * FROM PackageSample.NoShowMLRow WHERE ID < 1800
-- criar um conjunto de dados de teste
CREATE OR REPLACE VIEW PackageSample.NoShowMLRowTest AS SELECT * FROM PackageSample.NoShowMLRow WHERE ID >= 1800
-- evitar os erros no comando CREATE MODEL; ignore qualquer erro aqui
DROP MODEL NoShowModel
-- um modelo IntegratedML para a coluna de previsão noShow (não-apresentação) é criada a partir de outros modelos, utilizando o conjunto de dados PackageSample.NoShowMLRowTraining para a etapa de treinamento; o parâmetro "Semente" (seed) aqui destina-se à assegurar a reprodutibilidade dos resultados.
CREATE MODEL NoShowModel PREDICTING (Noshow) FROM PackageSample.NoShowMLRowTraining USING {"seed": 6}
-- o modelo é treinado, conforme definido no comando de criação de um modelo "CRIAR UM MODELO"
TRAIN MODEL NoShowModel
-- as informações sobre o modelo treinado são exibidas, como por exemplo o modelo ML selecionado pelo IntegratedML
SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS
-- a função de previsão (PREDICT) é utilizada para ver como usar o modelo nas intruções SQL
SELECT top 10 PREDICT(NoShowModel) AS PredictedNoshow, Noshow AS ActualNoshow FROM PackageSample.NoShowMLRowTest
-- uma validação em um conjunto de dados de teste é efetuado e as métricas de performance do modelo são calculadas
VALIDATE MODEL NoShowModel FROM PackageSample.NoShowMLRowTest
-- as métricas de performance são exibidas
SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS
Modelo de insuficiência cardíaca
-- criar um conjunto de dados de treinamento
CREATE OR REPLACE VIEW PackageSample.HeartFailureMLRowTraining AS SELECT DEATHEVENT,age,anaemia,creatininephosphokinase,diabetes,ejectionfraction,highbloodpressure,platelets,serumcreatinine,serumsodium,sex,smoking,followuptime FROM PackageSample.HeartFailureMLRow WHERE ID < 200
-- criar um conjunto de dados de teste
CREATE OR REPLACE VIEW PackageSample.HeartFailureMLRowTest AS SELECT DEATHEVENT,age,anaemia,creatininephosphokinase,diabetes,ejectionfraction,highbloodpressure,platelets,serumcreatinine,serumsodium,sex,smoking,followuptime FROM PackageSample.HeartFailureMLRow WHERE ID >= 200
-- evitar os erros no comando CREATE MODEL; ignorar todo erro aqui
DROP MODEL HeartFailureModel
-- as informações sobre o modelo formado são exibidas, como por exemplo o modelo ML selecionado pelo IntegratedML
CREATE MODEL HeartFailureModel PREDICTING (DEATHEVENT) FROM PackageSample.HeartFailureMLRowTraining USING {"seed": 6}
-- o modelo é treinado, conforme definido no comando de criação de um modelo "CRIAR UM MODELO"
TRAIN MODEL HeartFailureModel
-- as informações sobre o modelo treinado são exibidas, como por exemplo o modelo ML selecionado pelo IntegratedML
SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS
-- a função de previsão (PREDICT) é utilizada para ver como usar o modelo nas intruções SQL
SELECT top 10 PREDICT(HeartFailureModel) AS PredictedHeartFailure, DEATHEVENT AS ActualHeartFailure FROM PackageSample.HeartFailureMLRowTest
-- uma validação em um conjunto de dados de teste é efetuado e as métricas de performance do modelo são calculadas
VALIDATE MODEL HeartFailureModel FROM PackageSample.HeartFailureMLRowTest
-- as métricas de performance são exibidas
SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS
A última instrução SQL pode indicar os parâmetros de performance da classificação:
A mesma transformação pode ser aplicada para transformar os recursos FHIR provenientes dos sistemas externos através de uma API REST, por exemplo (veja o código):
Solução de problemas
Se você tiver erros durante as requisições API, indicando que o modelo não existe, é provável que alguma coisa anormal se produziu durante a criação do contêiner para os modelos de treinamento. Tente executar o método de treinamento novamente. Abra o terminal IRIS e execute:
ZN "FHIRSERVER"
Do ##class(PackageSample.Utils).TrainNoShowModel()
Do ##class(PackageSample.Utils).TrainHeartFailureModel()
Referências
Recursos FHIR utilizados como modelos: ttp://hl7.org/fhir/
Conjunto de dados para a formação de modelo de não-comparecimento: IntegratedML templat
Conjunto de dados para a formação de modelo de insuficiência cardíaca: kaggle