Limpar filtro
Artigo
ROBSON SERPA DA ROSA · Jun. 28, 2021
Uma VIEW em SQL é basicamente uma instrução SQL preparada.Deve ser executado e montado como qualquer outra consulta SQL.VIEW MATERIALIZADA significa que o conteúdo é coletado antes das mãos e pode ser recuperado com bastante rapidez.Eu vi o conceito primeiro com meu concorrente favorito chamado O * e eles fizeram muito barulho sobre isso. { favorite: because I could win every benchmark against them }
Dependendo da construção individual dessas views materializadas, atualizações e manutenção podem ser necessárias.No Caché / IRIS isso existe quase desde sempre e consideramos isso normal e um fato dado.A maioria dos desenvolvedores simplesmente não está ciente disso e, com um pouco de polimento, pode ser apresentado como um excelente recurso.Além disso, qualquer atualização e manutenção acontecem como uma funcionalidade integrada sem nenhum esforço extra.
Veja este exemplo:Em nossa famosa classe Sample.Person no namespace SAMPLES, estendi e reconstruí seu índice.
/// Define an index for <property>Name</property>.Index NameIDX On Name [ Data = (Name, Home.State, SSN) ];
E sendo experiente e confortável com o gerador de consultas, você sabe que
SELECT ID, Name, Home_State, SSN from Sample.Person
será executado rapidamente usando apenas o índice global ^ Sample.PersonI ("NameIDX")e nunca toque em seus dados globais.Essa é basicamente a funcionalidade de uma View materializada e a atualização está implícita.
Definido como VIEW (de MgmtPortal, já que Studio não é tão prático) você obtém esta classe.
Class Sample.Person.NameView [ ClassType = view , CompileAfter = Sample.Person , DdlAllowed , Not ProcedureBlock , SqlTableName = NameView , ViewQuery = { SELECT ID , Name , Home_State , SSN from Sample.Person } ] { Parameter READONLY = 1; }
Mas se você quiser um pouco mais de conforto, como um backlink para seus dados, pode mapear o próprio índice global.Portanto, você pode aplicar a sintaxe JOIN implícita e ter uma tabela totalmente funcional como aqui
SELECT Name, BaseClass->DOB, HomeState, SSN,"%CLASSNAME",BaseClass FROM Sample_Person.NameIDX
e aqui está a definição da classe e você deve projetá-la manualmente
/// mapped index /// Index NameIDX On Name [ Data = (Name, Home.State, SSN) ];Class Sample.Person.NameIDX Extends %Persistent [ Final ]{Property IndexName As %String [ InitialExpression = "NameIDX", ReadOnly ];Property SQLUPPERname As %String [ ReadOnly ];Property BaseClass As Sample.Person [ ReadOnly ];Index min On (IndexName, SQLUPPERname, BaseClass) [ IdKey ];/// Classname of Index SourceProperty %CLASSNAME As %String [ ReadOnly ];/// Person's name.Property Name As %String [ ReadOnly ];/// Person's home address. This uses an embedded object.Property HomeState As %String [ ReadOnly ];/// Person's Social Security number. This is validated using pattern match.Property SSN As %String(PATTERN = "3N1""-""2N1""-""4N") [ ReadOnly ];Parameter READONLY = 1;Parameter MANAGEDEXTENT As INTEGER = 0;Storage Default{ <Data name="NameIDXDefaultData"> <Value name="1"> <Value>%CLASSNAME</Value> </Value> <Value name="2"> <Value>Name</Value> </Value> <Value name="3"> <Value>HomeState</Value> </Value> <Value name="4"> <Value>SSN</Value> </Value> </Data> <DataLocation>^Sample.PersonI</DataLocation> <DefaultData>NameIDXDefaultData</DefaultData> <IdLocation>^Sample.Person.NameIDXD</IdLocation> <IndexLocation>^Sample.Person.NameIDXI</IndexLocation> <StreamLocation>^Sample.Person.NameIDXS</StreamLocation> <Type>%Library.CacheStorage</Type>} }
Este é um exemplo de codificação funcionando no Caché 2018.1.3 e IRIS 2020.2
Não será sincronizado com as novas versões
Também NÃO é atendido pelo Suporte da InterSystems!
Artigo
Danusa Calixto · Out. 27, 2022
Por que eu amo ObjectScript e por que eu acho que poderia amar Python ainda mais
Eu estava olhando o tópico de mensagens sobre o assunto "Desempenho ao construir uma string separada por vírgulas" e comecei a escrever uma resposta. No entanto, me distraí, a página foi atualizada e perdi meu texto. Não podia gastar tempo reescrevendo minha resposta, então comecei a escrever este documento em vez disso.
Comecei a escrever na linguagem MUMPS no início da minha carreira. Eu escrevia blocos de código bastante concisos e densos em que exercícios como o exemplo da string eram verdadeiros desafios. O desempenho dos servidores VAX ou Digital DEC eram aproveitados até a última gota. Planejávamos onde globais importantes ficariam em um disco. Quando o Caché foi lançado, ainda estávamos trabalhando com M/SQL. Durante um período, estive envolvido em diversas comparações de desempenho entre o Caché e o Oracle, Sybase e SQL Server. Criávamos um esquema de algumas tabelas, preenchidas com milhões de registros, e executávamos várias pesquisas no banco de dados resultante. Eu costumava escrever duas versões de declaração SQL. Uma era uma declaração SQL pura, e a outra era uma consulta personalizada que eu escrevia na definição de classe. A maior parte da lógica está no "Fetch", e eu elaborava esse método para maximizar os indíces definidos e usar ^CachéTemp para qualquer junção complexa de resultados provisórios. Às vezes, eu criava jobs de uma ou mais subconsultas que gerariam os globais temporários e resolvia as junções após a conclusão de todos os processos de jobs. O resultado poderia ser resumido da seguinte maneira:
Inserir dados no banco de dados usando SQL ou Caché Objects sempre foi mais rápido do que qualquer outro DB. Usar COS puro e conjuntos globais diretos era uma ordem de grandeza mais rápida do que SQL, Objects e qualquer outro banco de dados. O banco de dados resultante teria aproximadamente metade do tamanho de um criado por qualquer banco de dados relacional.
Ao comparar o código que escrevi no meu método "Fetch" com o código gerado pelo Caché SQL Engine, vi que usei menos variáveis, 25% menos linhas e o código ficou mais legível.
O número de leituras de blocos de dados físicos seria praticamente o mesmo que o código gerado pelo M/SQL. No entanto, o número de leituras lógicas do pool de buffers globais seria 20% menor que o M/SQL.
Recorri a todos os truques possíveis de desenvolvedores MUMPS. Usei comandos como "execute", "job" (criando threads para lidar com subconsultas em paralelo de forma eficaz), indireção e pós-condições. Recomendamos aos desenvolvedores que não usem esses recursos de linguagem para escrever código legível e capaz de ser mantido por outros desenvolvedores.
Eu inicializaria variáveis desta forma:
set (a,b,c,d)="",(x,y,z)=0,p1=+$h,p2=...,pN=99
Espremi o máximo de expressões possível em uma linha de código. Acreditávamos que a leitura de cada linha de código no "buffer de execução" resultava em um custo. Portanto, o número de linhas de código executadas sempre teve um efeito direto e inverso no desempenho.
Quando trabalho com código escrito por outro desenvolvedor e noto blocos de código que consistem em um comando definido por cada linha, fico um pouco agitado e sempre condenso essas 30 linhas em uma. Me apaixonei por Caché Objects. Vinte e cinco anos depois, esse caso durou mais que dois relacionamentos sérios e um casamento. Definições de classe, com nomes de propriedade precisos e bastante legíveis, indexação de bitmap em tudo, a menos que a indexação de busca possa fazer melhor. Quando possível, relacionamentos pais-filhos em vez de um-muitos. Uso uma chave primária personalizada em tabelas de código quando a indexação de bitmap não é necessária porque set record=$g(^global(code)) sempre será mais rápido que
set record="",rowId=$o(^IndexGlobal("IndexName",code,"")) set:$l(rowId) record=^Global(rowId)
Havia algumas formas de declarações SQL select com que o M/SQL não era compatível ou executava mal. Em geral, o Caché era 2 a 3 vezes mais rápido do que qualquer outro banco de dados.
Ao longo dos anos, o mecanismo SQL melhorou significativamente. A indexação de bitmap e iFind foi lançada. Usamos a indexação iFind nos nomes e endereços de pacientes em um banco de dados de 15 milhões de pessoas. Todos os outros campos são indexados por bitmap. Quando recebemos uma Pesquisa de Paciente de FHIR com vários parâmetros, oferecemos suporte a todos os qualificadores e operadores de especificação FHIR. Construímos uma declaração SQL que começa com uma junção em todas as entidades do paciente de FHIR, que armazenamos em classes persistentes. Estou reivindicando o uso do repositório IRIS for Health para nossa próxima fase de desenvolvimento. O IRIS teve dois lançamentos e amadureceu desde a primeira vez que trabalhei com ele na versão 2019.1. A junção é seguida por qualquer cláusula iFind em Nomes e Endereços, se especificada nos critérios de pesquisa. As cláusulas AND/OR são adicionadas para os campos nos critérios de pesquisa que sabemos serem compatíveis com índices bitmap. As pesquisas determinísticas ou probabilísticas que realizamos são tão rápidas e precisas que ainda me fazem pular de animação (na minha idade!!!).
Preciso confessar que nunca gostei do SQL quando fazia parte de um grupo cada vez menor de desenvolvedores que escreviam código MUMPS no final dos anos 80. Meus colegas foram rápidos em aderir ao Oracle ou SQL Server. Às vezes, era difícil não entrar em um estado de desespero quando ouvia opositores gritando: "O MUMPS está morto".
Então, na conferência anual do MUMPS em Dublin, acordamos em determinada manhã com um bilhete enfiado debaixo das nossas portas anunciando que a InterSystems havia comprado a DTM. Em uma conferência realizada um ano depois em Birmingham, eu estava trabalhando para a InterSystems e estávamos apresentando formulários do Visual Basic usando o Caché de dll que adquirimos quando compramos Data Tree. A Micronetics estava no estande em frente ao nosso, e eles não tinham um dll. O sistema de som deles era mais alto, mas nós havíamos vencido. Levaria mais um ano para comprarmos DSM da Digital e, por fim, MSM da Micronetics. Não havia mais como recuar. Lembro de mostrar o M/SQL a um cliente em Birmingham que escrevia um software de contabilidade. Um dos seus clientes era o Barings Bank, que havia acabado de perder 859.000.000 GBP devido ao trader desonesto Nick Leeson. Não pude deixar de configurar meu banco de dados de exemplo para poder executar uma consulta SQL que provavelmente não era mais complexa que "SELECT sum(Total) from Accounts WHERE .... and AccountNumber="666..". A conta tinha o mesmo número daquela usada por Nick Leeson para esconder as negociações que estava fazendo para recuperar sua situação, que piorava a cada toque do sino do pregão no mercado de ações de Singapura. Lembro de ficar rindo silenciosamente, em parte pela referência implícita ao colapso do Barings Bank, mas também porque a consulta foi realmente executada, forneceu a resposta correta e não levou mais de um minuto (nenhuma dessas coisas era uma certeza na época).
Essa é a única memória que tenho de gostar do SQL. Eu lidava com vários públicos de DBA do Oracle e SQL Server e demonstrava o Caché, Caché e VB, Caché Objects e Caché SQL. O Caché Objects me encantava: tão elegante, óbvio, maleável e legível. A sintaxe de objetos (em qualquer linguagem) é muito mais natural para mim do que qualquer declaração SQL. Quando tivemos a oportunidade de pegar o esquema do aplicativo de um cliente em potencial, executá-lo através do importador SQL e traduzir o conjunto de procedimentos armazenados que o cliente em potencial incluiria no esquema em Caché Objects ou Caché Globals puro, me familiarizei muito com a leitura do plano de execução SQL e a consulta armazenada gerada. Entrei em longas conversas com Ariel Klausner sobre a consulta SQL que o cliente em potencial me deu e não estava funcionando, e essa seria a diferença entre: ver os DBAs do Oracle saindo da sala de reuniões de volta para a segurança do ajuste do índice e as 6 horas garantidas de inatividade dos sistemas todos os dias durante os backups, onde eles poderiam trazer seus aplicativos relacionais de volta à vida em prontidão para os próximos dias de negociações, ou a emoção de conquistar um cliente que havíamos buscado por meses e estava mais interessado na velocidade do Caché, Orientação de Objetos, gateways .Net ou Java e a elegância simples do CSP de corretagem. Acredito que "por que escrever aplicativos DENTRO de um ambiente de DB?" não é uma pergunta. Primeiro, crio um banco de dados para meu código e outro para meus globais e, ali mesmo, tenho um ponto de separação. Todos nós crescemos nos últimos 25+ anos pensando em Classes, Objetos, ObjectScript e Globais como todos agrupados. Argumento que, no tempo de execução, o código sendo executado no buffer é OBJ. Basicamente, o código OBJ é uma mistura de código C compilado, código de máquina puro otimizado para a plataforma em que está sendo executado e alguns resquícios da definição de classe necessária se você estiver usando $classname, $classmethod, $property e outros fatores. Grande parte do "mecanismo" do Caché ou IRIS é escrito em ObjectScript, provando que essa é uma linguagem perfeita para trabalho. É uma linguagem que pode ser explícita, abreviada e muito compacta. Ela contém todos os operadores e as construções de qualquer linguagem moderna (if, ifelse, else, try - catch, while, for [para ser justo, nossa implementação de FOR é maravilhosa: | for i="apples","pears","Nigel","Fruit" {} | for {} | for i=$$$StartGValue():$$$Increment():$$$EndValue() | ]). Se um dos primeiros criadores do MUMPS tivesse chamado $order de "$next", ele seria imediatamente reconhecível como Next(), conforme encontrado em todas as outras linguagens que iteram por uma array. $PIECE é um pouco peculiar, mas só porque todos os outros bancos de dados usam campos de tamanho fixo. O conceito das strings delimitadas usadas como construção do banco de dados é estranho para um DBA do SQL. Ao analisar o código de máquina compilado de qualquer uma das formas de banco de dados, as instruções se movem pela string, caractere por caractere, e contando o número de caracteres ou fazendo isso enquanto procuram por um delimitador de campo específico.
$list conseguiu um desempenho um pouco melhor do que $piece, mas à custa de um ou dois bytes adicionais no início de cada campo. No entanto, ainda consumiu menos espaço do que os campos de comprimento fixo. O motivo pelo qual todo código de sistema é escrito em ObjectScript até hoje é porque a linguagem é muito eficiente e legível. Além disso, quando os principais desenvolvedores do Caché/IRIS, Scott Jones, Dave McCalldon e Mo Chung, precisavam de algo onde o ObjectScript fosse inadequado, eles escreviam em C e enterravam no Kernal.
Segundo, se eu tiver uma definição de tabela e campos que exigem alguma forma de formatação ou validação em cima e além das restrições óbvias de tipo e comprimento, então quero escrever esse código e mantê-lo bastante próximo à própria definição do campo. Por que eu entraria em outra linguagem, outro ambiente, para escrever essa validação? Os bancos de dados relacionais usam procedimentos e gatilhos armazenados para lidar com essa validação usando a linguagem SQL para expressar a lógica de validação. Se encontrar um programador que prefira usar SQL para escrever lógica complexa em vez de Basic, C#, C++, ObjectScript ou Python, eu compro uma cerveja para você na próxima vez que eu passar por Viena :-)
Na universidade, aprendi a programar em Fortran e Pascal. Pascal era uma linguagem utilizável e perfeitamente legível. Como um jovem de 19 anos que ficava facilmente empolgado, o fato de que o compilador Pascal pudesse ser escrito nessa linguagem me fascinou. Mais tarde, aprendi COBOL. WTF??? No entanto, tenho um amigo que é desenvolvedor da Sage Accounting e escreve nessa linguagem porque a Sage Accounting foi escrita em COBOL. Páginas e páginas da linguagem mais detalhada, ilegível e inutilizável que já vi. Na verdade, o COBOL ainda é muito usado.
Você poderia pensar que Pascal ultrapassaria facilmente COBAL e até Basic. Mas isso não aconteceu. Por quê? É simples. Essa linguagem não era usada nos aplicativos bancários (o COBOL foi amplamente adotado em grandes aplicativos de processamento em lotes de mainframe, como Accounting). Brincávamos dizendo que os bancos não queriam comprar o modelo Caché porque não era sofisticado o suficiente. Não era porque o ObjectScript não conseguisse fazer o processamento transacional desses aplicativos bancários. Nós éramos comprovadamente mais rápidos do que qualquer tecnologia que eles estivessem usando. O problema era que eles haviam gastado muito dinheiro nos sistemas que tinham e no hardware necessário para executar esses Lotes durante a noite para os bancos abrirem às 9h do dia seguinte. As salas caras dos servidores com gás radônio e sistemas de filtragem removem até mesmo as menores partículas de poeira para que não caiam em um disco ou uma fita magnética, causando a perda de um dia inteiro de transações de contas.
Pascal deveria ter vivido mais do que o Basic e talvez isso tivesse acontecido se a Microsoft não houvesse criado o Visual Basic e entrado em competição com Delphi e Borland. O IDE parecia mesmo com o VB, mas usava Pascal em vez de Basic. Tudo isso estava acontecendo enquanto a Microsoft lançava o C#, porque eles tinham que acomodar todos os programadores de C++ e certamente não conseguiriam conquistá-los com o Basic. Eles também estavam ameaçando lançar a própria versão do Java ou remover a compatibilidade com ele, porque o Java rodar em plataformas de hardware em que o Windows nunca seria capaz de rodar era algo que os incomodava. A Microsoft só recuou quando os avanços da tecnologia tornaram o conceito de máquinas virtuais ou containers uma opção de implantação realista. Então, Pascal e Delphi simplesmente desapareceram. Fiz uma pesquisa rápida no Google e há um interpretador de Pascal para Android, então ele ainda existe.
Considerando que Pascal era uma linguagem, ao contrário do Basic, que era apenas uma linguagem de certa forma. No entanto, ele foi usado pela Microsoft para os scripts de aplicativos como o Excel e uma conexão de propriedade com o SQL Server. Isso permitiu vincular dois ambientes que eram intrinsecamente inadequados sem o incômodo de atender aos padrões ODBC e JDBC. Os padrões eram fortemente apoiados pelo Oracle, Sybase e praticamente todos que precisavam fornecer um gateway para as versões proprietárias do SQL. Assim, o Basic sobreviveu. Estou feliz por ter começado minha carreira de programador com o Pascal. Depois, tive uma grande surpresa ao escrever programas COBOL por um ano trabalhando para uma empresa de seguros. Cheguei às costas úmidas e cinzentas do Reino Unido e comecei meu primeiro emprego, que, por acaso, usava MUMPS. Toda a evolução do MUMPS para CachéObjectScript, Objects, Object Gateways, .Net e Java, Caché Basic e MultiValueBasic e agora Python. O Python fecha um ciclo e, em certo sentido, prova que o ObjectScript não é uma aberração em uma tecnologia de banco de dados não relacional rejeitada.
Caché Globals são arrays esparsas multidimensionais tão convenientes para a própria natureza dos dados de saúde que não importa o esforço do Oracle e da Microsoft em consumir esse espaço no mercado. Ainda que tenham matado Pascal e Fortran, e até Basic, eles não conseguiram matar a InterSystems. Lembro de participar de um seminário do Oracle sobre "Oracle para a Saúde". A apresentadora falava sobre o Oracle na área da saúde que, ela nos garantiu, dominaria o mercado da saúde de uma vez por todas. Levantei minha mão e perguntei: "Não é isso o que vocês afirmam em todos os grandes lançamentos há anos? Vocês fracassaram antes. Por que fariam melhor desta vez?" Ela olhou para mim e perguntou, "Quem é você?". Respondi: "Sou da InterSystems. Dominamos o mercado da saúde há 35 anos. Conseguimos isso porque nossa tecnologia nasceu no Massachusetts General Hospital. E adivinhe. Eles ainda executam os sistemas centrais nas nossas tecnologias". Nessa hora, dois seguranças corpulentos me tiraram do auditório.
Então, o Oracle com o pSQL tem o Java. A Microsoft tem o SQL Server, C# e tSQL e, quando você precisa interagir com o Java, fica sujeito ao JDBC. Da mesma forma, com Java, se você precisa conversar com tabelas do SQL Server, é obrigado a usar ODBC. Onde ficamos? Bem, chegamos à grande ideia de ter wrappers para .Net e Java. Ao usar o ObjectScript, eu instancio uma instância da Classe A. Na verdade, não importa se a Classe A é uma classe .Net, Java ou ObjectScript, porque eu instancio esses objetos usando exatamente a mesma sintaxe em todos os casos. Depois, invoco os métodos de classe ou instância para manipular os objetos. O interior desses métodos não é importante, porque a sintaxe para interagir com essas classes e os métodos é basicamente idêntica, independentemente do que elas contenham.
Junto vem o Python, que compartilha vários recursos com o ObjectScript, pois é uma linguagem interpretada, e não compilada. É bastante legível e utilizável. Assim como o Caché ObjectScript encontrou um nicho nos dados não estruturados, o Python encontrou um nicho no mundo da modelagem matemática, ML, IA e muito, muito mais. Esse não é um mundo em que C# ou Java se sintam particularmente confortáveis. Aliás, nem o ObjectScript. Assim, a InterSystems foca em fornecer funcionalidades cada vez mais poderosas para manipular grandes quantidades de dados não estruturados, além de lançar iFind e iKnow, algumas técnicas de indexação muito inteligentes e algoritmos de correspondência de probabilidade. Então, você convida o Python para se aconchegar nas nossas arrays esparsas multidimensionais, trazendo milhões de bebês .py que fazem praticamente tudo o que você precisa. É a combinação perfeita. Por precaução, esqueci de mencionar que várias arquiteturas que dominam o mundo do desenvolvimento de páginas da Web são completamente baseadas em JS (Angular.js, REACT.js, Vue.js, Bootstrap — certo, não há JS, mas ele está em tudo menos no nome — e Node.js) e Arrays de JS. O JS não vai desaparecer tão cedo. No entanto, será interessante ver o que vai acontecer com a Golang, se é que você me entende. Vi entradas baseadas em arrays de JS nas últimas competições de código. Se existe uma tecnologia que entende arrays melhor do que qualquer outra, é a IRIS.
Penso naqueles dias, sentado no meu escritório na empresa onde trabalhava no coração de Londres. Em determinado momento, a empresa estava cheia de programadores MUMPS, mas ela os transformou em programadores SQL relacionais, que depois se tornaram redundantes. Lembro da sensação de começar a questionar se minha fé no MUMPS ser simplesmente a melhor linguagem que já havia encontrado poderia estar errada. A linguagem e as empresas que construíram interpretações dessa linguagem morreriam. Isso me deixou muito triste porque, até então, havia aprendido cinco outras linguagens de programação (APL, Basic, Fortran, COBOL e Pascal) antes de descobrir o MUMPS, e o MUMPS era tão simples. Fácil de escrever, ler e implantar. Em outras palavras, era tão natural para mim quanto o inglês, e tinha um ritmo parecido com os hinos que cantávamos na escola metodista que frequentei:
Onward, Christian soldiers!
Marching as to war,
With the cross of Jesus
Going on before.
Christ, the royal Master,
Leads against the foe;
Forward into battle,
See his banners go!
Mas ele não morreu. A música mudou um pouco:
A bordo, Nigel Saaalllm
Voando para a guerra
Com seu cartão de crédito internacional
Indo à frente.
John, o Mestre, McCormick
Lidera contra o inimigo (Microsoft)
Avançando na batalha
Veja seus duty frees partindo
CacheObjectScript era ainda melhor que o MUMPS se isso era possível. CacheObjects parecia tão legal quando demonstrado para uma audiência pelas primeiras vezes, e CacheSQL deixou seus dias de M/SQL para trás e melhorou muito ao longo dos anos. Ainda assim, eu particularmente não gosto de escrever muito em SQL, mas encontrei um bom equilíbrio entre Objects, SQL e referências globais diretas conforme relaxei. Meu código era bastante orientado para as referências globais diretas, com um pouco de OO e o mínimo de SQL. Quando vi com os produtos que o código gerado era compacto, elegante, eficiente e legível, o equilíbrio mudou novamente. Agora, uso nomes globais diretos raramente, muito Objects e uma quantidade razoável de SQL.
Para trabalhar com Python, minha mente precisará ver padrões diferentes do meu código ObjectScript. Há muitos "__abc__" e outras estruturas estranhas. No entanto, depois de escrever algumas páginas de código py e me afastar, como faço ao pintar a óleo, os padrões saltarão. Assim como vejo a música como sinestesia de cores, meus programas py codificados por cores fluindo pela página também começarão a parecer aquarela ou até mesmo uma pintura a óleo pesada. Ficarei encantado, e estará tudo bem. Danusa, parabéns pelo artigo faço um pouco de suas palavras na longa estrada de programação escrevendo em diversas linguagens (Cobol, Dataflex, Clipper, VB, C#, Pascal...), em meu primeiro contato com o MUMPS me encontrei e foi um pouco complicado trabalhavamos com dois Sistemas Operacionais onde pela manhã rodava programas Cobol e na tarde MUMPS no mesmo Servidor e foi assim que me apaixonei pelo código MUMPS pela simplicidade, facilidade de manutenção e muito leve, ao conhecer o CACHÉ 2.0 e programando em Pascal percebi o legado que poderia vir de bom ao passar dos anos, hoje trabalho com IRIS e sempre eu lia os artigos e comentários CACHÉ somente para Saúde, muito particularmente eu acho que não é bem assim, consegui escrever códigos em transações financeiras no qual colegas de profissões me diziam voce é um louco não vai conseguir com esse CACHÉ, finalizando esses meus códigos hoje faço uma autorização para cartões em 3 segundos usando IRIS e quanto aos concorrentes de 8 a 13 segundos, essa mágica esta no COS.
Artigo
Heloisa Paiva · Abr. 20
Sei que pessoas completamente novas no VS Code, Git, Docker, FHIR e outras ferramentas podem, às vezes, ter dificuldades com a configuração do ambiente. Por isso, decidi escrever um artigo que percorre todo o processo de configuração passo a passo para facilitar o início.
Eu realmente agradeceria se você pudesse deixar um comentário no final – me diga se as instruções foram claras, se algo ficou faltando ou se há mais alguma coisa que você acharia útil.
A configuração inclui:
✅ VS Code – Editor de código✅ Git – Sistema de controle de versão✅ Docker – Executa uma instância do IRIS for Health Community✅ Extensão REST Client do VS Code – Para executar consultas à API FHIR✅ Python – Para escrever scripts baseados em FHIR✅ Jupyter Notebooks –Para tarefas de IA e FHIR
Antes de começar: Certifique-se de ter privilégios de administrador no seu sistema.
Além de ler o guia, você também pode seguir os passos nos vídeos:
Para Windows
Para macOS
Há uma enquete no final do artigo, por favor, compartilhe seu progresso. Seu feedback é muito apreciado.
Então, vamos começar!
1. Instalar o Visual Studio Code (VS Code)
O VS Code será o editor principal para o desenvolvimento.
Windows & macOS
Acesse a página de download do VS Code: https://code.visualstudio.com/
Baixe o instalador para o seu sistema operacional:
Windows: arquivo .exe
macOS: arquivo .dmg
Execute o instalador e siga as instruções.
(Apenas Windows): Durante a instalação, marque a caixa para "Adicionar ao PATH".
Verificar a instalação:
Abra um terminal (Prompt de Comando, PowerShell ou Terminal do macOS)
Execute:
code --version
Você deverá ver o número da versão.
2. Instalar o Git
O Git é necessário para controle de versão, clonagem e gerenciamento de repositórios de código.
Windows
Baixe a versão mais recente em: https://git-scm.com/downloads
Execute o instalador:
Escolha "Use Git from the Windows Command Prompt".
Mantenha as configurações padrão e finalize a instalação.
Verifique a instalação:
git --version
macOS
Abra o terminal e execute:
git --version
Se o Git não estiver instalado, o macOS solicitará que você instale as Ferramentas de Linha de Comando. Siga as instruções.
3. Instalar o Docker
O Docker é necessário para executar o InterSystems IRIS for Health Community.
Windows
1. Baixe o Docker Desktop em: https://www.docker.com/products/docker-desktop2. Execute o instalador e siga a configuração.3. Reinicie seu computador após a instalação.4. Habilite o Backend WSL 2 (se solicitado).5. Verifique a instalação.
Observação importante: A instalação do Docker requer privilégios de administrador na sua máquina e pelo menos uma reinicialização.
macOS
1. Baixe o Docker Desktop para Mac em: https://www.docker.com/products/docker-desktop2. Instale-o arrastando o aplicativo Docker para a pasta Aplicativos.3. Abra o Docker no menu Aplicativos.
Para garantir que o motor do Docker Desktop esteja rodando no Windows ou macOS, siga estes passos:
Inicie o Docker Desktop
Windows: Abra o Docker Desktop no menu Iniciar. O ícone da baleia do Docker deve aparecer na sua bandeja do sistema.
Mac: Inicie o Docker Desktop na pasta Aplicativos. Você verá o ícone da baleia do Docker na barra de menus assim que estiver em execução.
Aguarde a Inicialização
Assim que você iniciar o Docker Desktop, o motor pode levar alguns instantes para começar. Procure por uma mensagem de status indicando que o Docker está "em execução" ou "iniciado".
Verifique via Terminal/Prompt de Comando::
Abra um terminal (ou Prompt de Comando/PowerShell no Windows) e execute:
docker --version
ou
docker info
Solução de problemas
Se o motor não estiver rodando, tente reiniciar o Docker Desktop ou verifique se há mensagens de erro na interface do Docker Desktop. Além disso, certifique-se de que seu sistema atende aos requisitos do Docker Desktop. Você pode ver mensagens de erro confusas que referenciam pipes se tentar construir uma imagem Docker sem o Docker Desktop em execução.
4. Construindo a imagem do IRIS for Health e executando-a usando o Docker
Antes de podermos iniciar um contêiner Docker executando o IRIS for Health Community (que inclui nosso servidor FHIR), devemos construí-lo.
Clone o repositório FHIR para um diretório conveniente no seu sistema de arquivos. Abra um terminal no VS Code e clone este repositório com o seguinte comando:
git clone https://github.com/pjamiesointersystems/Dockerfhir.git
Navegue até esse diretório e abra a pasta no VS Code. Siga as instruções no arquivo readme para construir e executar o contêiner. Uma etapa crucial é garantir que o repositório base esteja disponível no seu Docker. Você pode fazer isso através do comando no terminal do VS Code:
docker pull containers.intersystems.com/intersystems/irishealth-community:latest-em
Você deverá ver uma confirmação após alguns minutos.
Navegue até o diretório no VS Code onde você vê o arquivo docker-compose.yaml e então execute o comando:
docker-compose build
Isso iniciará o processo de construção, que pode levar até 10 minutos, durante os quais um repositório FHIR completo é construído e carregado com pacientes de amostra.
Após a conclusão do processo de construção, inicie o contêiner com o comando:
docker-compose up -d
seguido por
docker ps
Você deverá ver um contêiner chamado iris-fhir em execução. Se o contêiner não iniciar, verifique os logs:
docker logs iris-fhir
5. Instalar a Extensão REST Client do VS Code
Esta extensão permite que você envie requisições à API FHIR diretamente do VS Code.
Abra o VS Code
Vá para Extensões (Ctrl + Shift + X ou Cmd + Shift + X no macOS).
Procure por "REST Client". Existem diversos REST Clients, por favor, instale este:
Clique em Install.
6. Instalar o Python
O Python é necessário para tarefas de programação relacionadas ao FHIR.
Windows
1. Baixe o Python em: https://www.python.org/downloads/2. Execute o instalador e marque a caixa para "Add Python to PATH". Você precisará de credenciais de administrador para fazer modificações no Path.3. Conclua a instalação.4. Verifique a instalação:
python --version
macOS
Abra o Terminal e instale o Python via Homebrew:
Brew install python
Se você não tiver o Homebrew, instale-o primeiro
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Verifique a instalação:
python3 --version
7. Instalar o Jupyter Notebooks
Os Jupyter Notebooks são usados para tarefas de IA e FHIR, e tarefas de FHIR SQL.
Windows & macOS
Abra um terminal (Prompt de Comando, PowerShell ou Terminal do macOS).
Instale o Jupyter usando o pip:
pip install jupyter
jupyter --version
Execute o Jupyter Notebook:
jupyter notebook
Isso abrirá o Jupyter no seu navegador da web.
8. Validação
Execute seu contêiner navegando até o seu arquivo docker-compose no shell. Execute o comando:
docker compose up -d
docker ps
Acesse o Portal de Gerenciamento do IRIS:
Abra seu navegador e vá para:: http://localhost:8080/csp/sys/UtilHome.csp
Credenciais padrão:
Usuário: _SYSTEMSenha: ISCDEMO
Acesse a API FHIR:
Abra seu navegador e vá para: http://localhost:8080/csp/healthshare/demo/fhir/r4/metadata
Verificações Finais
Execute estes comandos para verificar todas as instalações:
code --version # VS Code
git --version # Git
docker --version # Docker
python --version # Python
jupyter --version # Jupyter
Se tudo funcionar, você instalou com sucesso todos os softwares acima.
Solução de problemas
Problema
Solução
"Command not found" para alguma ferramenta
Certifique-se de que ela foi adicionada ao PATH (reinstale se necessário).
Docker não está rodando no Windows
Reinicie o Docker Desktop e certifique-se de que o backend WSL 2 está habilitado.
Contêiner IRIS falha ao iniciar
Execute docker logs iris-fhir para verificar os erros.
Não consigo acessar a API FHIR
Certifique-se de que o contêiner está rodando (docker ps).
Obrigado pelo seu tempo. Aguardo ansiosamente a leitura dos seus comentários!
Artigo
Vinicius Maranhao Ribeiro de Castro · Jul. 6, 2021
Introdução
Suponha que você desenvolveu uma nova aplicação utilizando a parte de Interoperabilidade do InterSystems IRIS e você tem certeza de que será um sucesso! No entanto, você ainda não tem um número concreto de quantas pessoas irão utilizá-la. Além disso, pode haver dias específicos em que há mais pessoas utilizando sua aplicação e dias em que quase ninguém irá acessar. Deste modo, você necessita de que sua aplicação seja escalável!
O Kubernetes já nos ajuda bastante nesta tarefa com um componente chamado Horizontal Pod Autoscaler, que permite escalar horizontalmente uma aplicação baseado em uma métrica específica. Através do componente “Metrics Server” do próprio Kubernetes, é possível obter métricas como utilização de CPU ou de memória. Mas, e se você necessitar escalar sua aplicação utilizando outra métrica, como por exemplo o tamanho da fila de todos os “Business Hosts” presentes em uma determinada Produção da Interoperabilidade do IRIS? Por se tratar de uma métrica customizada, será necessário desenvolver um serviço que irá expor essa métrica customizada para o Kubernetes.
Nesta série de artigos, veremos em mais detalhes como funciona o processo de escalonamento horizontal do Kubernetes. Na parte 1, será abordado o funcionamento do componente responsável pelo escalonamento horizontal do Kubernetes e a criação e exposição de uma métrica customizada do IRIS no formato esperado pelo Prometheus. Já na parte 2, abordaremos como configurar o Prometheus para ler essa métrica e como configurar o prometheus-adapter para expor essa métrica ao Kubernetes.
O projeto utilizado para este artigo está totalmente disponível no GitHub em:
https://github.com/vmrcastro/iris-autoscale
Prometheus – Configurando a leitura destas métricas
Na parte 1 deste artigo, criamos e expusemos uma métrica customizada no IRIS. Agora, necessitamos configurar o Prometheus para ler essa métrica e armazenar em seu banco de dados. No repositório “iris-autoscale” do GitHub que estamos utilizando, já há um arquivo yaml pronto para fazer o deploy do Prometheus já com as configurações necessárias, no seguinte diretório:
prometheus/manifests/00-prometheus-deploy.yml
A parte mais importante deste arquivo yaml é o ConfigMap. Nele é onde definimos o arquivo prometheus.yml que contém as configurações do Prometheus. Este é o conteúdo do arquivo prometheus.yml:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
metrics_path: '/api/monitor/metrics'
# scheme defaults to 'http'.
kubernetes_sd_configs:
- role: pod
relabel_configs:
# Example relabel to scrape only pods that have
# "example.io/should_be_scraped = true" annotation.
# - source_labels: [__meta_kubernetes_pod_annotation_example_io_should_be_scraped]
# action: keep
# regex: true
#
# Example relabel to customize metric path based on pod
# "example.io/metric_path = <metric path>" annotation.
# - source_labels: [__meta_kubernetes_pod_annotation_example_io_metric_path]
# action: replace
# target_label: __metrics_path__
# regex: (.+)
#
# Example relabel to scrape only single, desired port for the pod
# based on pod "example.io/scrape_port = <port>" annotation.
# - source_labels: [__address__, __meta_kubernetes_pod_annotation_example_io_scrape_port]
# action: replace
# regex: ([^:]+)(?::\d+)?;(\d+)
# replacement: $1:$2
# target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
Não iremos abordar aqui todos os parâmetros definidos, até mesmo porque muitos já possuem um comentário autoexplicativo. No entanto, se restar alguma dúvida, é possível consultar a documentação oficial do Prometheus disponível em:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/
Vale ressaltar, no entanto, alguns parâmetros. Em “metrics_path”, vamos indicar o caminho da URL que o Prometheus irá enviar a requisição para buscar as métricas. Portanto, definimos “/api/monitor/metrics”. Desta forma, só nos resta indicar ao Prometheus qual é, ou quais são os endereços e portas do IRIS. No entanto, não podemos especificar um valor fixo para o hostname do(s) POD(s) do IRIS, já que eles não serão fixos! Lembrem-se que os PODs serão adicionados automaticamente mediante um aumento da fila e serão removidos também automaticamente mediante uma diminuição da fila. Por sorte, o Prometheus tem a capacidade de chamar as APIs do Kubernetes para buscar quais são os PODs presentes no cluster naquele momento, ficando sempre sincronizado com o estado do cluster de Kubernetes. Por isso que utilizamos a seção “kubernetes_sd_configs” na configuração do Prometheus. Como o Prometheus rodará no mesmo cluster de Kubernetes que estarão rodando os PODs que desejamos monitorar, não precisamos especificar nenhuma informação para o Prometheus de como se comunicar com o Kubernetes, tudo isso já será detectado automaticamente.
Outro ponto importante: quando tivermos com várias instâncias do IRIS rodando, cada uma delas terá sua métrica “queue_size”. Como saberemos no Prometheus qual instancia determinada métrica “queue_size” está se referindo? Podemos solucionar isso fazendo com que o Prometheus adicione em cada métrica um “label” para identificar de qual POD a métrica está se referindo. Por padrão, para cada POD que o Prometheus descobriu no cluster Kubernetes, há uma série de “meta labels” que podem ser incorporados nas métricas, através de configurações de “relabeling”. É isso que as seguintes linhas do arquivo de configuração do Prometheus fazem:
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
Neste caso, estamos adicionando para cada métrica, um label que se chama “kubernetes_namespace” que contém o nome do namespace do Kubernetes em que o POD está rodando e um label denominado “kubernetes_pod_name” que contém o nome do POD.
Com o Prometheus rodando, é possível verificar se as configurações estão corretas. Basta acessar a seguinte URL no seu browser:
http://<IP_do_Prometheus>:<Porta_do_Prometheus>/graph
Obs: Por padrão, é criado um “service” do Kubernetes do tipo Load Balancer para acessar o Prometheus. Você pode descobrir o IP do Prometheus verificando o campo “External IP” do service “prometheus-svc”.
É possível então, escrever o nome da métrica “queue_size” no campo de busca e clicar no botão “Execute”.
Ao clicar na aba “Graph”, se estiver tudo certo, você deverá visualizar uma linha amarela no valor 0, como mostra a figura abaixo.
Prometheus-Adapter - expondo métricas ao Kubernetes
Com o Prometheus configurado e coletando as métricas dos PODs do IRIS, agora só nos resta configurar nosso “metric API server”, no nosso caso o prometheus-adapter, para expor essas métricas de uma maneira que o Kubernetes entenda. Assim como no deploy do Prometheus, o deploy do prometheus-adapter no Kubernetes já está pronto, sendo realizado através dos arquivos yaml no diretório prometheus/manifests/ no repositório “iris-autoscale”. No entanto, vale a pena comentarmos alguns detalhes.
Primeiramente, para o prometheus adapter funcionar, é necessário especificar qual é o endereço e porta do Prometheus. Isso é especificado no arquivo “custom-metrics-apiserver-deployment.yaml”, no parâmetro “prometheus-url” na seção “args” do container “custom-metrics-apiserver”.
Assim como no deploy do Prometheus, as configurações do prometheus-adapter se encontram em um “configMap”, definido dentro do arquivo “custom-metrics-config-map.yaml”. Dentro do “configMap”, é definido o arquivo “config.yaml” que contém as seguintes configurações:
rules:
- seriesQuery: 'queue_size{kubernetes_namespace!="",kubernetes_pod_name!=""}'
resources:
overrides:
kubernetes_namespace: {resource: "namespace"}
kubernetes_pod_name: {resource: "pod"}
metricsQuery: '<<.Series>>{<<.LabelMatchers>>}'
Como é possível observar, em “seriesQuery” é definido a consulta que será enviada ao Prometheus para descobrir quais são as métricas disponíveis. Como neste exemplo só vamos trabalhar com a métrica “queue_size”, vamos especificá-la diretamente. Portanto, nesta query estamos buscando todas as métricas denominadas “queue_size” disponíveis, desde que “kubernetes_namespace” e “kubernetes_pod_name” não sejam nulos. Na seção “overrides”, vamos especificar ao prometheus-adapter de qual label deve ser extraído o namespace do Kubernetes e o nome do POD. Finalmente, em “metricsQuery”, vamos especificar qual é a consulta que desejamos enviar ao Prometheus para obter o valor da métrica a ser exposta para o Kubernetes. É possível, por exemplo, utilizar alguma função de agregação para transformar o valor antes de expô-la ao Kubernetes. No entanto, no nosso caso, queremos expor a métrica exatamente como ela se encontra no Prometheus, por isso especificamos apenas
metricsQuery: '<<.Series>>{<<.LabelMatchers>>}'
Ao final desta etapa é possível testar se está tudo certo simulando a requisição que o Horizontal Pod Autoscaler faria para a Custom Metrics API através do seguinte comando:
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/queue_size"
A resposta esperada seria algo do parecido com o seguinte:
{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/%2A/queue_size"
},
"items": [{
"describedObject": {
"kind": "Pod",
"namespace": "default",
"name": "iris-autoscale-5d4b546854-79gs7",
"apiVersion": "/v1"
},
"metricName": "queue_size",
"timestamp": "2021-05-03T18:31:03Z",
"value": "0",
"selector": null
}]
}
InterSystems IRIS - Produção criada para testes
A produção criada neste projeto é uma produção bem simples, apenas para testes. A ideia geral é expor um endpoint REST que ao receber uma requisição, envia uma mensagem assíncrona para qualquer um dos três Business Operations disponíveis (denominados “Teste 1”, “Teste 2” e “Teste 3”) de maneira randômica. Esses “Business Operations” são bem simples e, a única coisa que eles fazem é um “Hang” de 0 a 30 segundos, também de maneira randômica. Obviamente, essa produção não faz nenhum sentido, a ideia aqui é apenas simular o enfileiramento de mensagens no IRIS.
A requisição que deve ser enviada ao IRIS é a seguinte:
$ curl 'http://<IP_do_IRIS>:<Porta_WebServer_IRIS>/api/autoscale/request'
Se der tudo certo, a seguinte resposta retornará juntamente com um código HTTP status de “200 – OK”:
{"responseText":"The request has been sended!"}
Fazendo o deploy e testando o projeto
Para fazer o deploy do projeto em seu cluster de Kubernetes, basta clonar o repositório “iris-autoscale”, indicado na Introdução deste artigo e executar os seguintes comandos a partir da raiz do repositório clonado:
$ kubectl apply -f prometheus/manifests
$ kubectl apply -f kubernetes.yml
Para verificar se tudo correu bem, vamos dar uma olhada nos pods. Verifique se os seguintes PODs estão presentes com status de “Running” e com “READY 1/1”:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
iris-autoscale-5b9b77577f-jb2z4 1/1 Running 0 119m
prometheus-b974955c5-jwrhd 1/1 Running 0 119m
$ kubectl get pods -n custom-metrics
NAME READY STATUS RESTARTS AGE
custom-metrics-apiserver-776cf7cb8b-4v5b5 1/1 Running 0 121m
Se tudo correu bem, só nos resta testar o escalonamento automático. Para isso, utilizaremos o Collection Runner do PostMan para ficar enviando várias requisições ao Service do Kubernetes “iris-autoscale-svc”. Vamos enviar um total de 3000 requisições com um espaçamento de 2000 ms entre elas:
Podemos acompanhar as filas crescendo através da interface do Prometheus do lado esquerdo da tela e novos PODs do IRIS sendo criados e deletados automaticamente, como mostra o vídeo a seguir:
Conclusão
Neste artigo, abordamos todos os componentes necessários para escalar horizontalmente o InterSystems IRIS utilizando uma métrica customizada, no nosso caso a métrica “queue_size” que nos informa qual é o tamanho total das filas de uma produção em um namespace do IRIS. Por se tratar de uma métrica customizada, foi necessário fazer o deploy e configurar o Prometheus, para coletar essa métrica de cada instância de IRIS em execução, e o prometheus-adapter, para expor essa métrica no formato esperado pelo Kubernetes. Com isso, foi possível definir essa métrica na configuração do Kubernetes Horizontal Pod Autoscaler para que o escalonamento de nossas instâncias de IRIS seja realizado automaticamente.
Referências
https://learnk8s.io/autoscaling-apps-kubernetes
https://prometheus.io/docs/introduction/first_steps/
https://prometheus.io/docs/prometheus/latest/configuration/configuration/
https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/config-walkthrough.md
https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/walkthrough.md
https://github.com/prometheus/prometheus/blob/release-2.26/documentation/examples/prometheus-kubernetes.yml
Anúncio
Cristiano Silva · Out. 25, 2022
Eu gostaria de anunciar o lançamento de algo realmente bastante interessante - revolucionário na verdade. Isso pode soar exagerado, mas acho que você não viu nada parecido com isso, ou mesmo pensou que fosse possível!
Lançamos um novo módulo JavaScript/Node.js chamado glsdb, mais informações:
https://github.com/robtweed/glsdb
No entanto, para os propósitos deste anúncio aqui, quero apenas focar em uma parte do glsdb: suas APIs que abstraem classes IRIS (ou Caché) como objetos JavaScript equivalentes.
Vamos dar uma olhada rápida no que quero dizer com isso, porque quero dizer Objetos JavaScript que na verdade são Objetos IRIS que residem no banco de dados!
Suponha que eu instalei os dados SAMPLES do repositório InterSystems, por exemplo, conforme descrito aqui:
https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ASAMPLES
Então, para acessar esses dados SAMPLES usando o glsdb, primeiro preciso abrir uma conexão com o IRIS (o glsdb usa nossa interface mg-dbx para fazer essa conexão), por exemplo:
const {glsDB} = await import('glsdb');
let glsdb = new glsDB('iris');
let options = {
connection: 'network',
host: '172.17.0.2',
tcp_port: 7042,
username: "_SYSTEM",
password: "xxxxxx",
namespace: "SAMPLES"
}
glsdb.open(options);
Agora que fiz isso, posso fazer o seguinte para acessar um registro de Funcionário (Employee) no banco de dados SAMPLES:
let Employee = glsdb.irisClass('Sample.Employee');
let employee = Employee(101);
Até agora, tão surpreendente, talvez.
Mas é aqui que começa a diversão alucinante. Criamos um objeto JavaScript chamado employee que parece representar uma instância de Funcionário (Employee) com o OID 101, mas na verdade é um objeto JavaScript bastante especial: é um JavaScript Proxy Object que é mapeado diretamente para a instância física da classe IRIS no banco de dados. Eu tenho acesso direto a todas as propriedades e métodos da Classe IRIS, e quando eu manipulo o Proxy Object, na verdade estou manipulando a instância da Classe IRIS no banco de dados! Então agora eu posso fazer coisas como essa:
let salary = employee.Salary;
console.log('employee.Salary = ' + salary);
let company = employee.Company;
let name = company.Name;
console.log('Company: ' + name);
O importante a ser percebido aqui é que meu objeto employee não é uma cópia JavaScript na memória da instância da Classe IRIS - é a instância real da Classe Class, no banco de dados!
E como você pode ver acima, posso até encadear o relacionamento entre o Funcionário (Employee) e a Empresa (Company) como você faria em ObjectScript. Na verdade, eu posso até fazer isso de uma só vez:
console.log(employee.Company.Name);
e encadeie diretamente pelas propriedades, diretamente na Classe Física IRIS no banco de dados!
Eu também posso salvar as alterações no objeto - para isso eu uso um método especial chamado _save(), ex.: employee._save()
Para criar uma nova instância de um funcionário, eu simplesmente não especifico um OID, ex.:
let newEmployee = Employee();
glsdb fornece um atalho para definir e salvar um novo registro de uma só vez, usando o método _set(), ex.:
let ok = newEmployee._set({
Title: 'Manager'
Salary: 60000
})
Você pode até mesmo inspecionar os métodos e propriedades disponíveis da Classe:
console.log(employee._properties);
console.log(employee._methods);
Então, essa é uma visão geral do glsdb: espero que dê um gostinho de como é insano!
Você provavelmente está se perguntando como é possível fazer esse tipo de coisa.
Bem, em resumo, tudo se resume a um recurso relativamente novo do JavaScript: objetos proxy. Objetos Proxy são objetos especiais que atuam, como o próprio nome indica, como um proxy para outro objeto real. Mas o verdadeiro poder deles está no fato de que você pode definir "armadilhas" para acessar ou alterar as propriedades do Objeto Proxy e (e essa é a chave) você pode aplicar lógica personalizada a essas armadilhas. No caso do glsdb, essa lógica personalizada usa as APIs mg-dbx nos bastidores para realizar o acesso equivalente ou a alteração que está acontecendo no objeto Proxy para uma classe IRIS correspondente.
De qualquer forma, esse é um vislumbre rápido do mundo louco possível com o glsdb! O que eu publiquei é uma versão antecipada, então tenho certeza de que há aspectos do proxy IRIS que precisarão de ajustes. Se você quiser experimentá-lo e ver o que ele pode fazer, por favor, avise-me se você encontrar algo que não funcione adequadamente.
Ah, e a propósito, confira as outras APIs que o glsdb fornece - elas são igualmente interessantes e alucinantes!
E, finalmente, glsdb é um software de código aberto, então, por favor, brinque com o código e poste PRs no repositório do Github se você quiser ajudar no desenvolvimento posterior.
Artigo
Heloisa Paiva · Abr. 14
Introdução
O desempenho do banco de dados tornou-se um fator crítico de sucesso em um ambiente de aplicações moderno. Portanto, identificar e otimizar as consultas SQL que consomem mais recursos é essencial para garantir uma experiência de usuário fluida e manter a estabilidade da aplicação.
Este artigo explorará uma abordagem rápida para analisar as estatísticas de execução de consultas SQL em uma instância InterSystems IRIS para identificar áreas de otimização dentro de uma macro-aplicação.
Em vez de focar no monitoramento em tempo real, configuraremos um sistema que coleta e analisa estatísticas pré-calculadas pelo IRIS uma vez por hora. Essa abordagem, embora não permita o monitoramento instantâneo, oferece um excelente compromisso entre a riqueza de dados disponíveis e a simplicidade de implementação.
Usaremos o Grafana para visualização e análise de dados, o InfluxDB para armazenamento de séries temporais e o Telegraf para coleta de métricas. Essas ferramentas, reconhecidas por seu poder e flexibilidade, nos permitirão obter uma visão clara e explorável.
Mais especificamente, detalharemos a configuração do Telegraf para recuperar estatísticas. Também configuraremos a integração com o InfluxDB para armazenamento e análise de dados, e criaremos dashboards personalizados no Grafana. Isso nos ajudará a identificar rapidamente as consultas que exigem atenção especial.
Para facilitar a orquestração e o deploy desses vários componentes, empregaremos o Docker.
Pré-requisitos
Before you start, make sure you have the following:
Git: O Git é necessário para clonar o repositório do projeto que contém os arquivos de configuração e scripts..
Docker ou Docker Desktop: O Docker pode ser usado para conteinerizar as aplicações InfluxDB, Telegraf e Grafana, tornando-as mais fáceis de implantar e gerenciar.
InterSystems IRIS instance:Pelo menos a versão 2022.1, idealmente 2023.1 ou superior, com o pacote sql-stats-api instalado. Este pacote é essencial para expor as estatísticas SQL do IRIS e permitir que o Telegraf as colete. [ [Link to OpenExchange]
Embora nosso docker-compose inclua uma instância IRIS, ela não conterá nenhum dado de estatísticas SQL, pois será construída e iniciada do zero. Portanto, não será uma escolha conveniente para testar o sistema. É por isso que recomendamos fortemente que você tenha outra instância IRIS "ativa" (aquela com um histórico de consultas SQL) para ter a capacidade de visualizar dados reais e testar a ferramenta de análise.
Sobre as estatísticas
O IRIS coleta estatísticas de execução de consultas SQL com granularidade horária e diária. As estatísticas horárias identificam variações de desempenho ao longo do dia, enquanto as estatísticas diárias fornecem uma visão geral da atividade do banco de dados.
Abaixo, você pode ver os dados que coletaremos para cada consulta SQL:
Número de execuções: Indica o número de vezes que a consulta foi executada.
Tempo total de execução: Mede o tempo total de execução da consulta.
Variância dos tempos de execução: É usada para identificar variações de desempenho e problemas isolados.
Número total de linhas retornadas (RowCount): Disponível para IRIS 2023.1 e superior, esta métrica indica o número total de linhas retornadas pela consulta. Pode ajudá-lo a identificar consultas com alto consumo de recursos.
Número total de comandos executados: Também disponível para IRIS 2023.1 e superior, esta métrica facilita uma análise mais detalhada da atividade do banco de dados e identifica consultas que poderiam ser otimizadas pela redução do número de operações.
Essas informações podem ser acessadas através das seguintes tabelas:
INFORMATION_SCHEMA.STATEMENT_DAILY_STATS
INFORMATION_SCHEMA.STATEMENT_HOURLY_STATS
Essas tabelas estão disponíveis desde o IRIS 2022.1. Abaixo, apresentamos um exemplo de consulta SQL para recuperar as estatísticas:
SELECT ds.*
FROM INFORMATION_SCHEMA.STATEMENT_DAILY_STATS ds
INNER JOIN INFORMATION_SCHEMA.STATEMENTS st On ds.Statement = st.Hash
SELECT DATEADD('hh',"Hour",$PIECE(hs."Day",'||',2)) As DateTime, hs.*
FROM INFORMATION_SCHEMA.STATEMENT_HOURLY_STATS hs
INNER JOIN INFORMATION_SCHEMA.STATEMENTS st On $PIECE(hs."Day",'||',1) = st.Hash
Para versões anteriores ao IRIS 2022.1, recomendo fortemente o artigo de David Loveluck, que explica como recuperar estatísticas semelhantes.
Arquitetura
O projeto é baseado na interação de quatro componentes chave: IRIS, Grafana, InfluxDB e Telegraf. O diagrama abaixo ilustra a arquitetura geral do sistema e o fluxo de dados entre os vários componentes:
InterSystems IRIS:É a instância que utilizaremos para recuperar as estatísticas.
Pacote sql-stats-api: Este pacote ObjectScript expõe os dados estatísticos do IRIS via API REST. Ele oferece dois formatos de saída: JSON para uso geral e Line Protocol, um formato otimizado para ingestão rápida de dados de séries temporais no InfluxDB.
Telegraf: É um agente de coleta de métricas que fornece a ligação entre o IRIS e o InfluxDB. Neste projeto, utilizaremos duas instâncias do Telegraf:
Um agente consulta periodicamente a API REST do IRIS para recuperar as estatísticas SQL em tempo real.
Outro agente opera no modo de "varredura de diretório". Ele monitora um diretório com arquivos armazenados e os transmite para o InfluxDB, permitindo a integração de dados inacessíveis via API REST.
InfluxDB: Este banco de dados de séries temporais armazena e gerencia as estatísticas SQL coletadas pelo Telegraf, já que sua arquitetura é otimizada para esse tipo de dado. O InfluxDB também oferece integração nativa com o Grafana, facilitando a visualização e análise dos dados. Escolhemos o InfluxDB em vez do Prometheus porque o último é mais voltado para o monitoramento em tempo real e não é adequado para armazenar dados agregados, por exemplo, somas ou médias por hora ou dia, que são essenciais para nossa análise.
Grafana: É uma ferramenta de visualização que permite criar dashboards personalizados e interativos para analisar o desempenho do SQL. Ele recupera dados do InfluxDB e oferece uma variedade de gráficos e widgets para visualizar as estatísticas de forma clara e utilizável.
Instalação
Comece clonando o repositório:
git clone https://github.com/lscalese/iris-sql-dashboard.git
cd irisiris-sql-dashboard
Configurando o ambiente
Este projeto usa o Docker para orquestrar o Grafana, InfluxDB, Telegraf e IRIS. Por razões de segurança, informações confidenciais, como chaves de API e senhas, são armazenadas em um arquivo .env.
Crie o arquivo .env usando o exemplo fornecido abaixo:
cp .env.example .env
Edite o arquivo .env para configurar as variáveis:
Configuração das variáveis
TZ:Fuso horário. Você deve modificar esta variável de acordo com seu fuso horário para garantir que os dados tenham o carimbo de data/hora correto.
DOCKER_INFLUXDB_INIT_PASSWORD: Esta é a senha do Administrador para acessar o InfluxDB.
IRIS_USER: É um usuário IRIS da instância Docker do IRIS (_system por padrão).
IRIS_PASSWORD :É a senha da instância Docker do IRIS (SYS por padrão).
As chaves de API permitem as seguintes conexões:
GRAFANA_INFLUX_API_KEY : Grafana <-> InfluxDB.
TELEGRAF_INFLUX_API_KEY : Telegraf <-> InfluxDB.
Geração de Chaves de API
Por razões de segurança, o InfluxDB requer chaves de API para autenticação e autorização. Essas chaves são usadas para identificar e autorizar vários componentes (Telegraf, Grafana) a acessar o InfluxDB.
O script init-influxdb.sh, incluído no repositório, facilita a geração dessas chaves. Ele será executado automaticamente na primeira vez que o contêiner inflxudb2 for iniciado:
docker compose up -d influxdb2
Após alguns segundos, o arquivo .env será atualizado com suas chaves de API geradas.
Nota: esta etapa só deve ser realizada na primeira vez que você iniciar o contêiner..
Verifique se você tem acesso à interface de administração do InfluxDB através da URLhttp://localhost:8086/
Faça login com o nome de usuário "admin" e a senha especificada na variável de ambiente“DOCKER_INFLUXDB_INIT_PASSWORD” no arquivo ".env". Ao navegar em "Load Data >> Buckets", você deverá encontrar um bucket "IRIS_SQL_STATS" pré-configurado.
Ao acessar "Load Data >> API Tokens", você deverá encontrar nossas 2 chaves de API: "Grafana_IRIS_SQL_STATS" e "Telegraf_IRIS_SQL_STATS":
O ambiente está pronto agora e podemos passar para o próximo passo!
Início
Como o ambiente foi configurado e as chaves de API foram geradas, você pode finalmente iniciar o conjunto de contêineres. Para fazer isso, execute o seguinte comando no diretório raiz do projeto:
docker compose up -d
Este comando iniciará todos os serviços definidos no arquivo docker-compose.yml em segundo plano: InfluxDB, Telegraf, Grafana e a instância IRIS.
Dashboard Grafana
O Grafana agora está disponível em http://localhost:3000.
Conectar ao Grafana
Abra seu navegador da web e acesse http://localhost:3000. O nome de usuário e a senha padrão são admin/admin. No entanto, você será solicitado a alterar a senha no primeiro login.
Verificando a fonte de dados InfluxDB
A fonte de dados InfluxDB já está pré-configurada no Grafana. Tudo o que você precisa fazer é verificar se ela está funcionando corretamente.
Vá para "Connections > Data sources".
Você deverá ver uma fonte de dados chamada "influxdb".
Clique nela para modificá-la. Em seguida, clique em "Save & Test". A mensagem "Datasource is working. 1 bucket found" deverá aparecer na tela agora.
Explorando os painéis
Neste ponto, você verificou que a comunicação entre o Grafana e o InfluxDB está estabelecida, o que significa que você pode explorar os painéis predefinidos.
Prossiga para "Dashboards".
Você encontrará dois painéis predefinidos:
InfluxDB - SQL Stats: Este painel exibe estatísticas gerais de execução de consultas SQL, por exemplo, número de execuções, tempo total de execução e variação do tempo de execução.
InfluxDB - SQL Stats Details: Este painel fornece informações mais detalhadas sobre cada consulta SQL, por exemplo, o número total de linhas retornadas ou comandos executados.
Por que os painéis estão vazios?
Se você abrir os painéis, notará que eles estão vazios. Isso ocorre porque nosso agente Telegraf está atualmente conectado à instância IRIS fornecida no repositório Docker, que não contém dados estatísticos em suas tabelas. As estatísticas SQL são coletadas apenas se a instância IRIS estiver ativa e mantiver um histórico de consultas SQL.
Na próxima seção, exploraremos como injetar dados na instância IRIS para visualizar as estatísticas no Grafana.
Telegraf
O sistema de monitoramento opera dois agentes Telegraf com funções específicas:
telegraf-iris.conf: Este agente coleta dados em tempo real de uma instância IRIS ativa. Ele consulta a API REST do IRIS para recuperar estatísticas SQL e enviá-las para o InfluxDB.
telegraf-directory-scan.conf: Este agente integra dados históricos armazenados em arquivos. Ele monitora o diretório ./telegraf/in/, lê arquivos contendo estatísticas SQL e os envia para o InfluxDB.
Para coletar dados em tempo real, você deve conectar o Telegraf a uma instância IRIS ativa com o pacote sql-stats-api instalado. Este pacote expõe as estatísticas SQL por meio de uma API REST, permitindo que o Telegraf as acesse.
Configurando o telegraf-iris.conf
Para conectar o Telegraf à sua instância IRIS, você deve modificar o arquivo./telegraf/config/telegraf-iris.conf. Abaixo, você pode encontrar um exemplo da configuração:
[[inputs.http]]
## One or more URLs from which to read formatted metrics
urls = [
"http://iris:52773/csp/sqlstats/api/daily",
"http://iris:52773/csp/sqlstats/api/hourly"
]
## HTTP method
method = "GET"
## Optional HTTP headers
headers = {"Accept" = "text/plain"}
## Optional HTTP Basic Auth Credentials
username = "${IRIS_USER}"
password = "${IRIS_PASSWORD}"
data_format = "influx"
Certifique-se de que${IRIS_USER} e ${IRIS_PASSWORD}estejam definidos corretamente no seu arquivo.env .Nota: Você pode copiar o arquivo e editar os parâmetros para conectar o Telegraf a várias instâncias IRIS.
Reinicie o Telegraf:
Após modificar o arquivo de configuração, você precisa reiniciar o contêiner Telegraf para que as alterações tenham efeito:
docker compose up -d telegraf --force-recreate
Recuperando dados históricos
Para recuperar estatísticas históricas de SQL, use o método CreateInfluxFile do ObjectScript na sua instância IRIS:
; Adapt the path to your needs
Set sc = ##class(dc.sqlstats.services.SQLStats).CreateInfluxFile("/home/irisowner/dev/influxdb-lines.txt",,1)
Este script gravará o histórico de estatísticas de SQL em arquivos de texto, com um comprimento máximo de 10.000 linhas por arquivo. Em seguida, você pode colocar esses arquivos no diretório ./telegraf/in/ para processá-los e injetá-los no InfluxDB.
Verificando a injeção de dados
Você pode verificar se os dados foram injetados com precisão no InfluxDB com a ajuda da interface web. Vá para o "Data Explorer" e confira:
Visualização de dados no Grafana
Uma vez que os dados tenham sido injetados, você poderá visualizá-los nos seus painéis Grafana provisionados.
Chegamos ao final deste artigo. Espero que tenha sido útil para você e que tenha ensinado como configurar facilmente um sistema para monitorar e analisar estatísticas de SQL em suas instâncias IRIS.
Como você deve ter notado, este artigo se concentrou nos aspectos práticos da configuração e do emprego de várias ferramentas. Não pesquisamos todos os detalhes do funcionamento interno do InfluxDB, o formato do Line Protocol ou a linguagem de consulta Flux, nem examinamos a infinidade de plugins disponíveis para o Telegraf.
Esses tópicos, por mais fascinantes que sejam, exigiriam um artigo muito mais longo. Eu o(a) encorajo fortemente a consultar a documentação oficial para InfluxDB Começar com o InfluxDB e o Diretório de Pluginsdo Telegraf para aprofundar seu conhecimento e descobrir todas as possibilidades oferecidas por essas ferramentas.
Não hesite em compartilhar suas experiências nos comentários.
Obrigado por ler e até a próxima!
Artigo
Jhonata Rebouças · Maio 1, 2021
O mesmo serviço com a possibilidade de receber várias consultas SQL diferentes e sempre entregar o resultado independente de quantas colunas distintas tenham essas diferentes consultas. Aqui demonstro como pode ser possível montar esse tipo de serviço utilizando o Service Bus da Intersystems.
Possível cenário (Desconsiderar o uso de um BI):
Vamos pensar em um painel real time onde iremos fornecer as informações de consumo de um material por região para o setor de compras e teremos as informações do nome do produto, fabricante e quantidade por exemplo.
O problema começa quando pensamos que esse painel será modularizado e posteriormente teremos a necessidade de atender aos outros setores que buscam informações totalmente diferentes, afinal um setor como relacionamento pode necessitar de informações relacionadas aos clientes. Nesse caso, criar um novo serviço totalmente do zero seria um grande desperdício de recurso e consequentemente financeiro.
Agora que temos o cenário podemos pensar em uma solução e no caso vamos utilizar o Ensemble da intersystems.O payload deve ser pensado para expandir e suportar o máximo de colunas possíveis imaginando que um momento pode atender a uma demanda de duas colunas e em outra pode atender a uma demanda de nove ou mais colunas.A sugestão é adicionar essas colunas em um array e seus dados em outro array relacionado dessa forma pode haver uma flexibilização em sua estrutura.
{
"columns":[
{
"description": "String", //Nome da coluna
"data": [
{
"value": "String" //Valor da coluna
}
]
}
]
}
Utilizando o Ensemble e SQL:
Vamos definir a montagem dos objetos e mensagens que serão necessários para criar um payload semelhante, com a adição de algumas informações que serão úteis a identificação do painel, mas podem ser eliminadas ou adaptadas dependendo da necessidade.Será adicionado informações relacionadas a identificação e descrição do painel que pode ser útil no layout.Não será abordado o passo a passo da criação dos objetos e mensagens pois não possuem nada de diferente do padrão. *disponível no git hub
O Business Operation
Possui um pequeno detalhe que fará a grande diferença. Não é necessário se preocupar com as informações adicionais sobre o painel, somente com a consulta SQL. No exemplo usamos uma consulta direta na base do caché, porém é possível utilizar bancos como Oracle, MySql, SqlServer e outros que pode inclusive utilizar Stored Procedures que se torna mais organizado e eficiente.Ao realizar a consulta é interessante ter conhecimento de quantas colunas essas esse painel retornará e a consulta deverá respeitar esse valor e ordem para que seja montado um retorno correto.
For i=1:1:pRequest.columns {
Set sc=result.Execute()
While result.Next(.sc) {
Set tData = ##class(IRIS.JHONATA.OBJ.Reports.Row.row).%New()
Set tData.row = result.Get("row"_i)
Do pResponse.data.Insert(tData)
}
}
Note que o resultado da consulta é esperado como um alias sequenciado, result.Get("row"_i), comparando com a quantidade de colunas garantimos que todos os dados serão recebidos de forma ordenada antes de passar para a próxima coluna.A consulta ficará semelhante:
select name as row1,
code as row2,
description as row3
from table
No Operation é interessante receber a procedure ou query como propriedade no request, isso porque poderemos enviar essa informação amarrada a uma regra criada posteriormente e não teremos a necessidade de alterar a classe ou mesmo reiniciar a cada novo painel criado.
O Business Process
No process temos bastantes detalhes para permitir que tenhamos a adição de novas consultas para os painéis que por ventura forem surgindo sem a necessidade de interferir no que já está em produção. Para não ficar ainda mais extenso, vamos focar somente em algumas atividades principais.Utilizamos um foreach para percorrer os resultados e contabilizar o seu número total de resultados que será importante para identificar em que momento a informação passa a ser pertencente à próxima coluna.
Outro ponto importante é a utilização de um Data Transformation para organizar a lista de resultados em colunas com seus respectivos dados associados à coluna correta.
Para que não seja necessário parar o serviço ou impactar seu uso a cada nova funcionalidade ou painel toda parte de consulta ou procedure deve ficar em uma regra identificada pelo tópico do projeto, essa será a única informação necessária no request. Nessa regra pode estar contido informações estáticas do serviço como descrição e nome do painel.O nome das colunas também devem estar presentes na regra em uma única string com delimitadores e na mesma ordem das colunas retornadas na query para que possa ser contado e utilizado na lógica do Data transformation.
Resultado final:
Pode ser utilizado de várias maneiras, por exemplo através de um serviço REST que informará somente o TOPICO desejado.Na demonstração existe somente uma regra com uma consulta realizada no caché, e em seguida é gerada uma nova regra com uma nova consulta que já entra pronto para funcionar sem interrupção dos demais.
O Projeto
Todo o projeto pode ser baixado no GIT HUB https://github.com/jhonatabf/iris_universal_report e a tabela de exemplo está na pasta OBJ/Persistent
Artigo
Angelo Bruno Braga · Out. 4, 2022
Olá Comunidade,
Deseja obter ajuda, discutir um recurso interessante, fazer um anúncio ou compartilhar seu conhecimento? Neste post, vamos dizer-lhe como fazer tudo isso.
Para facilitar a navegação neste "como fazer" basta seguir o conteúdo:
Diretrizes Gerais
Pergunta
Artigo ou Anúncio
Discussão
Diretrizes Gerais
Para começar, você precisa clicar no botão "Nova postagem" no menu superior do site da Comunidade de desenvolvedores:
Depois disso, você verá o editor que lhe dará a opção de criar uma Pergunta, um Anúncio, um Artigo ou uma Discussão. Diferentes tipos de postagens têm seus próprios conjuntos de campos obrigatórios e opcionais.
Primeiro, vamos falar sobre os campos comuns para todos os tipos de postagens e, em seguida, passar para os detalhes.
Basicamente, cada post tem um Título*, Corpo*, Grupo*, Tags e algumas opções adicionais onde você pode adicionar uma enquete ou anexar arquivos PDF. Todos os campos de texto marcados com asteriscos (*) são obrigatórios. Então, primeiro, você precisa escolher o tipo de postagem, pode ser como mencionado acima uma Pergunta, um Anúncio, um Artigo ou uma Discussão.
Em seguida, formule a ideia principal da sua pergunta da maneira mais precisa e concisa e escreva-a como um Título.
Depois disso, no Corpo, você escreve o que quiser compartilhar com os outros. Existem duas opções para escolher ao escrever o texto do seu post. Você pode usar o editor WYSIWYG ou Markdown. Ambos vão te dar a mesma coisa como resultado, é claro.
vs.
Depois de terminar de escrever o texto, você deve escolher o Grupo, que geralmente é a tecnologia, produto ou serviço fornecido pela InterSystems.
Após o campo Grupo, há um campo Tags onde você pode adicionar tags relacionadas ao conteúdo do seu post. Existem várias tags, então escolha com responsabilidade, porque outras pessoas usam essas tags para procurar ou classificar as informações quando estão pesquisando algo.
Abaixo das Tags, há um link para ver mais opções. Lá você pode anexar um documento em pdf (por exemplo, o horário do evento em formato pdf) e fornecer o nome que deseja que seja exibido.
Outra coisa que você pode fazer através de mais opções é Adicionar uma enquete. Preencha os campos com uma pergunta, possíveis respostas, escolha a duração, etc.
Depois de terminar, você pode Visualizar sua postagem para ver como ela ficará para outras pessoas, Salvá-la para continuar editando mais tarde ou Publicá-la imediatamente.
Além disso, você pode agendar uma publicação do seu post. Basta clicar na seta para baixo, escolher Agendar postagem e definir a data e a hora.
Depois de tudo definido, basta clicar em Agendar postagem e pronto.
Basicamente, isso é para a funcionalidade comum de criar uma postagem.
Pergunta
Pelo nome, é óbvio que você deve escolher esse tipo de postagem se precisar da ajuda de alguém. Aqui, na Comunidade de desenvolvedores, há muitos especialistas e alguém já deve ter se deparado com a mesma situação. Então não hesite em fazer perguntas ou respondê-las ;)
Para pedir ajuda, por favor, formule a ideia principal da sua pergunta e escreva-a como um título. Em seguida, selecione a Versão do produto que você está usando, pois versões diferentes têm funcionalidades e classes diferentes e alguns conselhos podem ser úteis para algumas versões e inúteis para outras.
Para ser ainda mais preciso, você pode fornecer a compilação completa que está usando atualmente utilizando o comando $ZV no terminal. Para obter sua versão completa, você pode abrir o Terminal e executar o comando:
write $ZV
O mesmo pode ser feito no IDE que você está usando ou pode ser vista no Portal de Gerenciamento:
Os demais campos são os mesmos descritos anteriormente.
Artigo or Anúncio
Para compartilhar seu conhecimento ou fazer um anúncio você deve escolher um tipo de postagem de Artigo ou Anúncio respectivamente. Esses tipos de postagens, além dos campos comuns, também possuem alguns adicionais. Esses são o Post Anterior, o Próximo Post e o link do aplicativo Open Exchange.
Então, basicamente, se este artigo/anúncio atual (ou um ramo de uma discussão) estiver vinculado a outro, você pode adicionar o link no campo Postagem anterior e, como resultado, outros membros da comunidade verão as seguintes postagens relacionadas bloquear no final do post.
Você não precisa abrir a postagem anterior e adicionar o link para uma próxima postagem, isso será feito automaticamente.
Se sua postagem tiver um projeto no Open Exchange vinculado a ela, você poderá adicionar o link a este projeto em um campo respectivo.
Discussão
Para iniciar uma conversa sobre algum recurso ou outro ou talvez compartilhar sua experiência com o uso da tecnologia e pedir feedback, você pode iniciar uma Discussão. Esse tipo de postagem tem todos os campos comuns e também os links para Post anterior e Próximo post.
É isso
Isto é o que você precisa saber para iniciar um novo tópico na Comunidade. Boa sorte
E estamos ansiosos para ler seus pensamentos ;
Artigo
Heloisa Paiva · Maio 12
Ao trabalhar com InterSystems IRIS, desenvolvedores e arquitetos de banco de dados frequentemente enfrentam uma decisão crítica: usar Dynamic SQL ou Embedded SQL para consultar e atualizar dados. Ambos os métodos têm seus pontos fortes e casos de uso únicos, mas entender suas implicações de desempenho é essencial para fazer a escolha certa. O tempo de resposta, uma métrica chave na avaliação do desempenho de aplicações, pode variar significativamente dependendo da abordagem SQL utilizada. Dynamic SQL oferece flexibilidade, pois as consultas podem ser construídas e executadas em tempo de execução, tornando-o ideal para cenários com necessidades de consulta imprevisíveis ou altamente variáveis. Por outro lado, Embedded SQL enfatiza a estabilidade e a eficiência ao integrar código SQL diretamente na lógica da aplicação, oferecendo tempos de resposta otimizados para padrões de consulta predefinidos.
Neste artigo, explorarei os tempos de resposta ao usar esses dois tipos de SQL e como eles dependem de diferentes estruturas de classe e do uso de parâmetros. Para fazer isso, usarei as seguintes classes do diagrama:
Para testar os tempos, criei o seguinte número de objetos para cada classe:
Paciente - 50 milhões
Visita - 150 milhões
Médico - 500 mil
Endereço - 50 milhões
Dessa forma, esperava ver um tempo razoável para executar as consultas e observar as diferenças entre a execução de Embedded SQL e Dynamic SQL. O único índice que adicionei foi o índice automático de um-para-muitos para o relacionamento Médico-Visita.
Então, vamos dar uma olhada nas consultas que vou executar e, em seguida, na duração da execução:
select count(*) from Hospital.Address
select count(*) from Hospital.Address where State = :param
select count(*) from Hospital.Patient p left join Hospital.Address a on p.address = a.id
select count(*) from Hospital.Patient p left join Hospital.Address a on p.address = a.id where a.State = :param
select count(a.Address->State) from Hospital.Patient a
select count(*) from Hospital.Patient p where p.Address->State = :param
select count(p.Visit->VisitDate) from Hospital.Patient p
select count(*) from Hospital.Patient p where p.Visit->VisitDate > :param
select count(v.Patient->Name) from Hospital.Visit v
select count(*) from Hospital.Visit v where v.Patient->Name %startswith :param
select count(v.Patient->Address->State) from Hospital.Visit v
select count(*) from Hospital.Visit v where v.Patient->Address->State = :param
select count(v.Doctor->Name) from Hospital.Visit v
select count(*) from Hospital.Visit v where v.Doctor->Name %startswith :param
select count(*) into :p from Hospital.Visit v where v.Doctor->Name %startswith :param and v.Patient->Name %startswith :param
select count(*) into :p from Hospital.Visit v where v.Doctor->Name %startswith :param and v.Patient->Name %startswith :param and v.Patient->Address->State = :param1
Obviamente, o código acima é a sintaxe para Embedded SQL (porque há parâmetros nomeados). Para Dynamic SQL, as consultas são quase as mesmas, mas em vez de parâmetros nomeados, tenho parâmetros não nomeados 😉. Por exemplo, para a última, tenho a seguinte consulta:
select count(*) from Hospital.Visit v where v.Doctor->Name %startswith ? and v.Patient->Name %startswith ? and v.Patient->Address->State = ?
Agora, vamos dar uma olhada nos resultados:
No of query
Embedded SQL (sec)
Dynamic SQL (sec)
1
49
12
2
3
3
3
32
26
4
47
46
5
48
46
6
47
46
7
1767
1806
8
1768
1841
9
31
26
10
83
81
11
41
45
12
73
71
13
23
26
14
1
1
15
2
2
16
3
3
Podemos observar um valor discrepante colossal, que é a primeira consulta. O Embedded SQL levou muito mais tempo para executar do que o Dynamic SQL. Executar as mesmas consultas várias vezes me deu resultados mais ou menos iguais. Então, é o que é.
Em geral, podemos ver que o relacionamento pai-filho é muito mais lento do lado da propriedade dos filhos, mesmo que todos os dados de Paciente e Visita estejam armazenados no global de Paciente. O índice salvou o dia para o relacionamento de um-para-muitos, e o tempo de execução foi consideravelmente menor. No geral, o tempo de resposta é quase sempre similar e difere em menos de 10%; às vezes, é o mesmo. Claro, usei consultas simples que não demoraram muito para preparar, então essa etapa pôde ser quase ignorada.
Artigo
Danusa Calixto · jan 11, 2023
Como você deve se lembrar do Global Summit 2022 ou do 2022.2 launch webinar, estamos lançando um novo e empolgante recurso para incluir em suas soluções analíticas no InterSystems IRIS. O Armazenamento Colunar apresenta uma maneira alternativa de armazenar os dados da tabela SQL que oferece uma aceleração de ordem de grandeza para consultas analíticas. Lançado pela primeira vez como um recurso experimental em 2022.2, o mais recente Developer Preview 2022.3 inclui várias atualizações que achamos que valeriam uma postagem rápida aqui.
Uma rápida recapitulação
Se você não está familiarizado com o Armazenamento Colunar, por favor dê uma olhada neste vídeo de breve introdução ou na sessão do GS2022 sobre o assunto. Resumindo, estamos codificando os dados da tabela em blocos de 64 mil valores por coluna usando um novo tipo de dado $vector. $vector é um tipo de dados interno (por enquanto) que utiliza esquemas de codificação adaptáveis para permitir o armazenamento eficiente de dados esparsos e densos. A codificação também é otimizada para um monte de operações $vector dedicadas, como para calcular agregados, agrupamentos e filtros de partes inteiras de 64k valores por vez, aproveitando instruções SIMD onde possível.
No momento da consulta SQL, aproveitamos essas operações criando um plano de consulta que também opera nesses blocos, o que, como você pode imaginar, gera uma redução massiva na quantidade de IO e no número de instruções ObjectScript para executar sua consulta, em comparação com o clássico processamento linha a linha. É claro que os IOs individuais são maiores e as operações $vector são um pouco mais pesadas do que as contrapartes de valor único do mundo orientado a linha, mas os ganhos são enormes. Usamos o termo planos de consulta vetorizados para estratégias de execução que lidam com dados $vector, empurrando blocos inteiros por meio de uma cadeia de operações individuais rápidas.
Apenas mais rápido
Mais importante ainda, as coisas ficaram mais rápidas. O novo kit inclui várias alterações na pilha que melhoram o desempenho, desde otimizações até aquelas operações $vector de baixo nível sobre alguns aprimoramentos de processamento de consulta e um conjunto mais amplo de planos de consulta vetorizados que podem ser paralelizados. Certas formas de carregar dados, como por meio de instruções INSERT .. SELECT, agora também empregarão um modelo de buffer que já usamos para criar índices e agora permitem a construção de tabelas inteiras com alto desempenho.
JOINs vetorizados
O recurso mais empolgante que adicionamos nesta versão é o suporte para unir dados colunares de maneira vetorizada. Em 2022.2, quando você deseja combinar dados de duas tabelas em uma consulta, ainda recorremos a uma estratégia robusta de JOIN linha por linha que funciona tanto em dados organizados em colunas quanto em linhas. Agora, quando ambas as extremidades do JOIN são armazenadas em formato colunar, usamos uma nova API do kernel para JOIN na memória, mantendo seu formato $vector. Este é outro passo importante para planos de consulta totalmente vetorizados, mesmo para as consultas mais complexas.
Aqui está um exemplo de uma consulta que aproveita a nova função, fazendo um self-JOIN do conjunto de dados New York Taxi que usamos em demonstrações anteriores:
SELECT
COUNT(*),
MAX(r1.total_amount - r2.total_amount)
FROM
NYTaxi.Rides r1,
NYTaxi.Rides r2
WHERE
r1.DOLocationID = r2.PULocationID
AND r1.tpep_dropoff_datetime = r2.tpep_pickup_datetime
AND r2.DOLocationID = r1.PULocationID
AND r1.passenger_count > 2
AND r2.passenger_count > 2
Esta consulta procura pares de viagens com mais de 2 passageiros, onde a segunda viagem começou onde a primeira terminou, exatamente ao mesmo tempo e onde a segunda viagem levou um de volta para onde a primeira começou. Não é uma análise superútil, mas eu só tinha uma tabela real neste esquema e a chave JOIN composta tornou isso um pouco menos trivial. No plano de consulta dessa instrução, você verá trechos como Apply vector operation %VHASH (para construir a chave JOIN composta) e Read vector-join temp-file A, que indicam nosso novo marceneiro vetorizado em ação! Isso pode soar como uma pepita pequena e trivial em um plano de consulta longo, mas envolve muita engenharia inteligente internamente, e há alguns fornecedores de bancos de dados colunares de alto nível por aí que simplesmente não permitem nenhum dos isso e coloque severas restrições em seu layout de esquema, então, por favor, JUNTE-SE a nós para aproveitar isso! :-)
Quando o plano de consulta lê esse arquivo temporário, você pode perceber que ainda há algum processamento linha por linha no trabalho pós-junção, o que nos leva a...
O que vem a seguir?
O Armazenamento Colunar ainda está marcado como "experimental" em 2022.3, mas estamos nos aproximando da prontidão de produção e da vetorização completa de ponta a ponta para consultas de várias tabelas. Isso inclui o trabalho pós-junção mencionado acima, suporte mais amplo no otimizador de consulta, carregamento ainda mais rápido de tabelas colunares e aprimoramentos adicionais no joiner, como suporte a memória compartilhada. Resumindo: agora é um ótimo momento para dar uma primeira chance a tudo isso com o conjunto de dados de taxi de Nova York (agora em IPM ou com script docker) usando o 2022.3 Community Edition, então você só precisa pressionar "Executar" quando lançarmos o 2023.1!
Se você estiver interessado em obter conselhos mais personalizados sobre como aproveitar o armazenamento colunar com seus próprios dados e consultas, entre em contato comigo ou com sua equipe de contas diretamente e talvez nos encontremos no Global Summit 2023 ;-).
Artigo
Danusa Calixto · Mar. 26
A interface de usuário de interoperabilidade agora inclui experiências de usuário modernizadas para os aplicativos Editor DTL e Configuração da Produção que estão disponíveis para aceitação em todos os produtos de interoperabilidade. Você pode alternar entre as visualizações modernizada e padrão. Todas as outras telas de interoperabilidade permanecem na interface de usuário padrão. Observe que as alterações são limitadas a esses dois aplicativos e identificamos abaixo a funcionalidade que está disponível atualmente.
Para experimentar as novas telas antes de atualizar, você pode baixar a versão 2025.1 da nossa página do kit da comunidade aqui: https://evaluation.intersystems.com/Eval/. Revise este breve tutorial sobre os aprimoramentos feitos nessas telas assistindo ao Building Integrations: A New User Experience do Learning Service!
Configuração da Produção - Introdução às Tarefas de Configuração
Configuração da Produção: Suportado nesta versão da Configuração de Produção:
Criando/Editando/Copiando/Excluindo Hosts
Parar/Iniciar Hosts
Editar Configurações da Produção
Parar/Iniciar Produções
Integração de controle de origem: Suporte para integração de controle de origem para a funcionalidade de configuração acima está disponível.
Visualização de Painel Dividido: Os usuários podem abrir o Editor de Regras e o Editor DTL diretamente na tela Configuração de Produção para visualizar e editar regras e transformações incluídas na produção em uma exibição de painel dividido.
Filtros Aprimorados: uma caixa de pesquisa na parte superior permite que você pesquise e filtre em todos os componentes de negócios, incluindo várias categorias, DTLs e subtransformações. Use a barra lateral esquerda para pesquisar independentemente do painel principal para visualizar os resultados da pesquisa em hosts e categorias.
Edição em massa de Categorias de Hosts: você pode adicionar uma nova categoria ou editar uma categoria existente para uma produção adicionando hosts da configuração de produção.
Roteadores expansíveis: Os roteadores podem ser expandidos para visualizar todas as regras, transformações e conexões em linha.
Conexões de Host Retrabalhadas: conexões diretas e indiretas agora são renderizadas quando um host comercial é selecionado, permitindo que você veja o caminho completo que uma mensagem pode tomar. Passe o mouse sobre qualquer host de saída ou entrada para diferenciar ainda mais as conexões. Mostrar Somente Hosts Conectados filtrará apenas os hosts selecionados e suas conexões.
Editor DTL - Introdução às Ferramentas DTL
Integração a Controle de Fontes: suporte para integração de controle de fonte está disponível.
Integração com VS Code: Os usuários podem visualizar esta versão do Editor DTL no VS Code IDE.
Suporte a Python embutido: O suporte ao Python Embutido se estende a esta versão do Editor DTL.
Teste DTL : O utilitário de Teste de DTL está disponível nesta versão do Editor DTL.
Trocar o Layout do Painel: O editor DTL suporta um layout lado a lado e de cima para baixo. Clique no botão de layout na faixa superior para experimentar isso.
Desfazer/Refazer: Os usuários podem desfazer e refazer todas as ações com os botões desfazer/refazer que ainda não foram salvos no código.
Parâmetro Gerar Segmentos Vazios: o parâmetro GENERATEEMPTYSEGMENTS está disponível para gerar segmentos vazios para campos ausentes.
Visualização de Subtransformações: Os usuários podem visualizar as subtransformações clicando no ícone de olho para abrir o DTL da subtransformação em uma nova guia.
Rolagem:
Rolagem Independente: As seções esquerda e direita (origem e destino) do DTL podem ser roladas independentemente posicionando o cursor acima de uma das seções e usando a roda de rolagem ou o trackpad para mover os segmentos verticalmente.
Rolagem Conjunta: as seções de origem e de destino podem ser roladas conjuntamente colocando o cursor no meio do diagrama.
Preenchimento Automático de Campo: Preenchimento automático disponível para: campos 'origem', 'destino' e 'condição', bem como na Classe de origem, Tipo de documento de origem, Classe de destino, Tipo de documento de destino.
Numeração Ordinal: O editor visual permite que você ative ou desative a visualização dos números ordinais e da expressão de caminho completo para cada segmento.
Referências Fáceis: Quando um campo no Editor de Ações está em foco, clicar duas vezes em um segmento no Editor Gráfico insere a referência de segmento correspondente na posição atual do cursor no Editor de Ações.
Sincronização: Clique em um elemento no editor visual para destacar a linha correspondente no editor de ações.
📣 CHAMADA PARA AÇÃO 📣
Se você tiver algum feedback, envie-o das seguintes formas:
✨ NOVOS Recursos em toda a interoperabilidade: Insira uma ideia no portal de ideias ou envolva-se com outras ideias no Portal de Ideias da InterSystems. Para novas ideias, adicione a tag "Interoperabilidade" em sua postagem ou vote positivamente em recursos já propostos na lista!
💻 Feedback Geral da Experiência do Usuário em todas as soluções de interoperabilidade: Por favor, comente seu feedback ou interaja com outros comentários abaixo.
🗒 Sugestões/Feedback sobre aplicativos modernizados (conforme descrito acima): Comente seu feedback ou interaja com outros comentários abaixo.
Por favor, considere completar a oportunidade do Global Master para se envolver com a equipe em uma sessão de feedback guiada privada e ganhar pontos! Inscreva-se para essas sessões via Global Masters >> aqui.
Se você quiser fornecer algum feedback adicional em um formato privado, envie seus pensamentos ou perguntas por e-mail para: ux@intersystems.com
Artigo
Heloisa Paiva · Abr. 3
No artigo anterior, apresentamos o aplicativo d[IA]gnosis, desenvolvido para auxiliar na codificação de diagnósticos na CID-10. Neste artigo, veremos como o InterSystems IRIS for Health nos fornece as ferramentas necessárias para a geração de vetores a partir da lista de códigos da CID-10, usando um modelo de linguagem pré-treinado, seu armazenamento e a subsequente busca por similaridades em todos esses vetores gerados.
Introdução
Uma das principais características que surgiram com o desenvolvimento de modelos de IA é o que conhecemos como RAG (Geração Aumentada por Recuperação), que nos permite melhorar os resultados dos modelos LLM ao incorporar um contexto ao modelo. Bem, em nosso exemplo, o contexto é dado pelo conjunto de diagnósticos da CID-10 e, para usá-los, devemos primeiro vetorizá-los.
Como vetorizar nossa lista de diagnósticos?
SentenceTransformers e Embedded Python
Para a geração de vetores, utilizamos a biblioteca Python SentenceTransformers , que facilita muito a vetorização de textos livres a partir de modelos pré-treinados. Do próprio site deles:
Sentence Transformers (também conhecido como SBERT) é o módulo Python de referência para acessar, usar e treinar modelos de incorporação de texto e imagem de última geração. Pode ser usado para calcular incorporações usando modelos Sentence Transformer (início rápido) ou para calcular pontuações de similaridade usando modelos Cross-Encoder (início rápido). Isso desbloqueia uma ampla gama de aplicações, incluindo busca semântica, similaridade textual semântica, e mineração de paráfrases.
Dentre todos os modelos desenvolvidos pela comunidade SentenceTransformers, encontramos o BioLORD-2023-M, um modelo pré-treinado que gerará vetores de 786 dimensões.
Este modelo foi treinado usando o BioLORD, uma nova estratégia de pré-treinamento para produzir representações significativas para frases clínicas e conceitos biomédicos.
As metodologias de ponta operam maximizando a similaridade na representação de nomes que se referem ao mesmo conceito e evitando o colapso por meio do aprendizado contrastivo. No entanto, como os nomes biomédicos nem sempre são autoexplicativos, às vezes resulta em representações não semânticas.
O BioLORD supera esse problema fundamentando suas representações de conceitos usando definições, bem como descrições curtas derivadas de um grafo de conhecimento multirrelacional que consiste em ontologias biomédicas. Graças a essa fundamentação, nosso modelo produz representações de conceitos mais semânticas que correspondem mais de perto à estrutura hierárquica das ontologias. O BioLORD-2023 estabelece um novo estado da arte para similaridade de texto tanto em frases clínicas (MedSTS) quanto em conceitos biomédicos (EHR-Rel-B).
Como você pode ver em sua definição, este modelo é pré-treinado com conceitos médicos que serão úteis ao vetorizar tanto nossos códigos CID-10 quanto texto livre.
Para o nosso projeto, baixaremos este modelo para acelerar a criação de vetores:
if not os.path.isdir('/shared/model/'):
model = sentence_transformers.SentenceTransformer('FremyCompany/BioLORD-2023-M')
model.save('/shared/model/')
Uma vez em nossa equipe, podemos inserir os textos a serem vetorizados em listas para acelerar o processo. Vamos ver como vetorizamos os códigos CID-10 que registramos anteriormente em nossa classe ENCODER.Object.Codes.
st = iris.sql.prepare("SELECT TOP 50 CodeId, Description FROM ENCODER_Object.Codes WHERE VectorDescription is null ORDER BY ID ASC ")
resultSet = st.execute()
df = resultSet.dataframe()
if (df.size > 0):
model = sentence_transformers.SentenceTransformer("/shared/model/")
embeddings = model.encode(df['description'].tolist(), normalize_embeddings=True)
df['vectordescription'] = embeddings.tolist()
stmt = iris.sql.prepare("UPDATE ENCODER_Object.Codes SET VectorDescription = TO_VECTOR(?,DECIMAL) WHERE CodeId = ?")
for index, row in df.iterrows():
rs = stmt.execute(str(row['vectordescription']), row['codeid'])
else:
flagLoop = False
Como você pode ver, primeiro extraímos os códigos armazenados em nossa tabela de códigos CID-10 que ainda não vetorizamos, mas que registramos em uma etapa anterior após extraí-los do arquivo CSV, depois extraímos a lista de descrições a serem vetorizadas e, usando a biblioteca Python sentence_transformers, recuperaremos nosso modelo e geraremos os embeddings associados.
Finalmente, atualizaremos o código CID-10 com a descrição vetorizada executando o UPDATE. Como você pode ver, o comando para vetorizar o resultado retornado pelo modelo é o comando SQL TO_VECTOR no IRIS.
Usando-o no IRIS
Ok, temos nosso código Python, então só precisamos envolvê-lo em uma classe que estende Ens.BusinessProcess e incluí-la em nossa produção, depois conectá-la ao Business Service encarregado de recuperar o arquivo CSV e pronto!
Vamos dar uma olhada em como esse código ficará em nossa produção:
Como você pode ver, temos nosso Business Service com o adaptador EnsLib.File.InboundAdapter, que nos permitirá coletar o arquivo de códigos e redirecioná-lo para nosso Business Process, no qual realizaremos todas as operações de vetorização e armazenamento, fornecendo-nos um conjunto de registros como o seguinte
Agora nosso aplicativo estaria pronto para começar a procurar possíveis correspondências com os textos que enviamos a ele!
No próximo artigo...
No próximo artigo, mostraremos como o front-end do aplicativo desenvolvido em Angular 17 é integrado à nossa produção no IRIS for Health e como o IRIS recebe os textos a serem analisados, os vetoriza e busca similaridades na tabela de códigos CID-10.
Não perca!
Artigo
Mikhail Khomenko · Nov. 19, 2021
[Da última vez](https://community.intersystems.com/post/deploying-intersystems-iris-solution-gcp-kubernetes-cluster-gke-using-circleci), lançamos uma aplicação IRIS no Google Cloud usando seu serviço GKE.
E, embora criar um cluster manualmente (ou por meio do [gcloud](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-cluster)) seja fácil, a [abordagem de Infraestrutura como Código (IaC)](https://martinfowler.com/bliki/InfrastructureAsCode.html) moderna recomenda que a descrição do cluster Kubernetes também seja armazenada no repositório como código. Como escrever este código é determinado pela ferramenta que é usada para IaC.
No caso do Google Cloud, existem várias opções, entre elas o Deployment Manager e o Terraform. As opiniões estão divididas quanto o que é melhor: se você quiser saber mais, leia este tópico no Reddit Opiniões sobre Terraform vs. Deployment Manager? e o artigo no Medium Comparando o GCP Deployment Manager e o Terraform.
Para este artigo, escolheremos o Terraform, já que ele está menos vinculado a um fornecedor específico e você pode usar seu IaC com diferentes provedores em nuvem.
Suporemos que você leu o artigo anterior e já tem uma conta do Google, e que criou um projeto chamado “Desenvolvimento”, como no artigo anterior. Neste artigo, seu ID é mostrado como . Nos exemplos abaixo, altere-o para o ID de seu próprio projeto.
Lembre-se de que o Google não é gratuito, embora tenha um nível gratuito. Certifique-se de controlar suas despesas.
Também presumiremos que você já bifurcou o repositório original. Chamaremos essa bifurcação (fork) de “my-objectscript-rest-docker-template” e nos referiremos ao seu diretório raiz como"" ao longo deste artigo.
Todos os exemplos de código são armazenados neste repositório para simplificar a cópia e a colagem.
O diagrama a seguir descreve todo o processo de implantação em uma imagem:
Então, vamos instalar a versão mais recente do Terraform no momento desta postagem:
$ terraform versionTerraform v0.12.17
A versão é importante aqui, pois muitos exemplos na Internet usam versões anteriores, e a 0.12 trouxe muitas mudanças.
Queremos que o Terraform execute certas ações (use certas APIs) em nossa conta do GCP. Para ativar isso, crie uma conta de serviço com o nome 'terraform' e ative a API do Kubernetes Engine. Não se preocupe sobre como vamos conseguir isso — basta continuar lendo e suas perguntas serão respondidas.
Vamos tentar um exemplo com o utilitário gcloud, embora também possamos usar o console web.
Usaremos alguns comandos diferentes nos exemplos a seguir. Consulte os tópicos da documentação a seguir para obter mais detalhes sobre esses comandos e recursos.
Como criar contas de serviço IAM no gcloud
Como atribuir papéis a uma conta de serviço para recursos específicos
Como criar chaves de conta de serviço IAM no gcloud
Como ativar uma API no projeto do Google Cloud
Agora vamos analisar o exemplo.
$ gcloud init
Como trabalhamos com o gcloud no artigo anterior, não discutiremos todos os detalhes de configuração aqui. Para este exemplo, execute os seguintes comandos:
$ cd $ mkdir terraform; cd terraform$ gcloud iam service-accounts create terraform --description "Terraform" --display-name "terraform"
Agora, vamos adicionar alguns papéis à conta de serviço do terraform além de “Administrador do Kubernetes Engine” (container.admin). Essas funções serão úteis para nós no futuro.
$ gcloud projects add-iam-policy-binding \ --member serviceAccount:terraform@.iam.gserviceaccount.com \ --role roles/container.admin
$ gcloud projects add-iam-policy-binding \ --member serviceAccount:terraform@.iam.gserviceaccount.com \ --role roles/iam.serviceAccountUser
$ gcloud projects add-iam-policy-binding \ --member serviceAccount:terraform@.iam.gserviceaccount.com \ --role roles/compute.viewer
$ gcloud projects add-iam-policy-binding \ --member serviceAccount:terraform@.iam.gserviceaccount.com \ --role roles/storage.admin
$ gcloud iam service-accounts keys create account.json \--iam-account terraform@.iam.gserviceaccount.com
Observe que a última entrada cria o seu arquivo account.json. Certifique-se de manter este arquivo em segredo.
$ gcloud projects list$ gcloud config set project $ gcloud services list --available | grep 'Kubernetes Engine'$ gcloud services enable container.googleapis.com$ gcloud services list --enabled | grep 'Kubernetes Engine'container.googleapis.com Kubernetes Engine API
A seguir, vamos descrever o cluster GKE na linguagem HCL do Terraform. Observe que usamos vários placeholders aqui; substitua-os por seus valores:
Placeholder
Significado
Exemplo
ID do projeto do GCP
possible-symbol-254507
Armazenamento para estado/bloqueio do Terraform - deve ser único
circleci-gke-terraform-demo
Região onde os recursos serão criados
europe-west1
Zona onde os recursos serão criados
europe-west1-b
Nome do cluster GKE
dev-cluster
Nome do pool de nós de trabalho do GKE
dev-cluster-node-pool
Aqui está a configuração HCL para o cluster na prática:
$ cat main.tfterraform { required_version = "~> 0.12" backend "gcs" { bucket = "" prefix = "terraform/state" credentials = "account.json" }}
provider "google" { credentials = file("account.json") project = "" region = ""}
resource "google_container_cluster" "gke-cluster" { name = "" location = "" remove_default_node_pool = true # No cluster regional (localização é região, não zona) # este é um número de nós por zona initial_node_count = 1}
resource "google_container_node_pool" "preemptible_node_pool" { name = "" location = "" cluster = google_container_cluster.gke-cluster.name # No cluster regional (localização é região, não zona) # este é um número de nós por zona node_count = 1
node_config { preemptible = true machine_type = "n1-standard-1" oauth_scopes = [ "storage-ro", "logging-write", "monitoring" ] }}
Para garantir que o código HCL esteja no formato adequado, o Terraform fornece um comando de formatação útil que você pode usar:
$ terraform fmt
O fragmento de código (snippet) mostrado acima indica que os recursos criados serão fornecidos pelo Google e os próprios recursos são google_container_cluster e google_container_node_pool, que designamos como preemptivos para economia de custos. Também optamos por criar nosso próprio pool em vez de usar o padrão.
Vamos nos concentrar brevemente na seguinte configuração:
terraform { required_version = "~> 0.12" backend "gcs" { Bucket = "" Prefix = "terraform/state" credentials = "account.json" }}
O Terraform grava tudo o que é feito no arquivo de status e usa esse arquivo para outro trabalho. Para um compartilhamento conveniente, é melhor armazenar este arquivo em algum lugar remoto. Um lugar típico é um Google Bucket.
Vamos criar este bucket. Use o nome do seu bucket em vez do placeholder . Antes da criação do bucket, vamos verificar se está disponível, pois deve ser único em todo o GCP:
$ gsutil acl get gs://
Boa resposta:
BucketNotFoundException: 404 gs:// bucket does not exist
A resposta ocupado "Busy" significa que você deve escolher outro nome:
AccessDeniedException: 403 does not have storage.buckets.get access to
Também vamos habilitar o controle de versão, como o Terraform recomenda.
$ gsutil mb -l EU gs://
$ gsutil versioning get gs://gs://: Suspended
$ gsutil versioning set on gs://
$ gsutil versioning get gs://gs://: Enabled
O Terraform é modular e precisa adicionar um plugin de provedor do Google para criar algo no GCP. Usamos o seguinte comando para fazer isso:
$ terraform init
Vejamos o que o Terraform fará para criar um cluster do GKE:
$ terraform plan -out dev-cluster.plan
A saída do comando inclui detalhes do plano. Se você não tem objeções, vamos implementar este plano:
$ terraform apply dev-cluster.plan
A propósito, para excluir os recursos criados pelo Terraform, execute este comando a partir do diretório /terraform/:
$ terraform destroy -auto-approve
Vamos deixar o cluster como está por um tempo e seguir em frente. Mas primeiro observe que não queremos colocar tudo no repositório, então vamos adicionar vários arquivos às exceções:
$ cat /.gitignore.DS_Storeterraform/.terraform/terraform/*.planterraform/*.json
Usando Helm
No artigo anterior, armazenamos os manifestos do Kubernetes como arquivos YAML no diretório /k8s/, que enviamos ao cluster usando o comando "kubectl apply".
Desta vez, tentaremos uma abordagem diferente: usando o gerenciador de pacotes Helm do Kubernetes, que foi atualizado recentemente para a versão 3. Use a versão 3 ou posterior porque a versão 2 tinha problemas de segurança do lado do Kubernetes (veja Executando o Helm na produção: melhores práticas de Segurança para mais detalhes). Primeiro, empacotaremos os manifestos Kubernetes de nosso diretório k8s/ em um pacote Helm, que é conhecido como chart. Um chart Helm instalado no Kubernetes é chamado de release. Em uma configuração mínima, um chart consistirá em vários arquivos:
$ mkdir /helm; cd /helm$ tree /helm/helm/├── Chart.yaml├── templates│ ├── deployment.yaml│ ├── _helpers.tpl│ └── service.yaml└── values.yaml
Seu propósito está bem descrito no site oficial. As práticas recomendadas para criar seus próprios charts são descritas no Guia de Melhores Práticas do Chart na documentação do Helm.
Esta é a aparência do conteúdo de nossos arquivos:
$ cat Chart.yamlapiVersion: v2name: iris-restversion: 0.1.0appVersion: 1.0.3description: Helm for ObjectScript-REST-Docker-template applicationsources:- https://github.com/intersystems-community/objectscript-rest-docker-template- https://github.com/intersystems-community/gke-terraform-circleci-objectscript-rest-docker-template
$ cat templates/deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: {{ template "iris-rest.name" . }} labels: app: {{ template "iris-rest.name" . }} chart: {{ template "iris-rest.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }}spec: replicas: {{ .Values.replicaCount }} strategy: {{- .Values.strategy | nindent 4 }} selector: matchLabels: app: {{ template "iris-rest.name" . }} release: {{ .Release.Name }} template: metadata: labels: app: {{ template "iris-rest.name" . }} release: {{ .Release.Name }} spec: containers: - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} name: {{ template "iris-rest.name" . }} ports: - containerPort: {{ .Values.webPort.value }} name: {{ .Values.webPort.name }}
$ cat templates/service.yaml{{- if .Values.service.enabled }}apiVersion: v1kind: Servicemetadata: name: {{ .Values.service.name }} labels: app: {{ template "iris-rest.name" . }} chart: {{ template "iris-rest.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }}spec: selector: app: {{ template "iris-rest.name" . }} release: {{ .Release.Name }} ports: {{- range $key, $value := .Values.service.ports }} - name: {{ $key }}{{ toYaml $value | indent 6 }} {{- end }} type: {{ .Values.service.type }} {{- if ne .Values.service.loadBalancerIP "" }} loadBalancerIP: {{ .Values.service.loadBalancerIP }} {{- end }}{{- end }}
$ cat templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}{{/*Expande o nome do chart.*/}}
{{- define "iris-rest.name" -}}{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}{{- end -}}
{{/*Cria o nome e a versão do chart conforme usado pelo rótulo do chart.*/}}{{- define "iris-rest.chart" -}}{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}{{- end -}}
$ cat values.yamlnamespaceOverride: iris-rest
replicaCount: 1
strategy: | type: Recreate
image: repository: eu.gcr.io/iris-rest tag: v1
webPort: name: web value: 52773
service: enabled: true name: iris-rest type: LoadBalancer loadBalancerIP: "" ports: web: port: 52773 targetPort: 52773 protocol: TCP
Para criar os charts do Helm, instale o cliente Helm e o utilitário de linha de comando kubectl.
$ helm versionversion.BuildInfo{Version:"v3.0.1", GitCommit:"7c22ef9ce89e0ebeb7125ba2ebf7d421f3e82ffa", GitTreeState:"clean", GoVersion:"go1.13.4"}
Crie um namespace chamado iris. Seria bom se isso fosse criado durante a implantação, mas até agora não é o caso.
Primeiro, adicione credenciais para o cluster criado pelo Terraform ao kube-config:
$ gcloud container clusters get-credentials --zone --project $ kubectl create ns iris
Confirme (sem iniciar uma implantação real) se o Helm criará o seguinte no Kubernetes:
$ cd /helm$ helm upgrade iris-rest \ --install \ . \ --namespace iris \ --debug \ --dry-run
A saída — os manifestos do Kubernetes — foi omitida por causa do espaço aqui. Se tudo estiver certo, vamos implantar:
$ helm upgrade iris-rest --install . --namespace iris$ helm list -n iris --allIris-rest iris 1 2019-12-14 15:24:19.292227564 +0200 EET deployed iris-rest-0.1.0 1.0.3
Vemos que o Helm implantou nossa aplicação, mas como ainda não criamos a imagem Docker eu.gcr.io/iris-rest:v1, o Kubernetes não pode extraí-la (ImagePullBackOff):
$ kubectl -n iris get poNAME READY STATUS RESTARTS AGEiris-rest-59b748c577-6cnrt 0/1 ImagePullBackOff 0 10m
Vamos terminar com isso por agora:
$ helm delete iris-rest -n iris
O Lado do CircleCI
Agora que experimentamos o Terraform e o cliente Helm, vamos colocá-los em uso durante o processo de implantação no lado do CircleCI.
$ cat /.circleci/config.ymlversion: 2.1
orbs: gcp-gcr: circleci/gcp-gcr@0.6.1
jobs: terraform: docker: # A versão da imagem do Terraform deve ser a mesma de quando # você executa o terraform antes da máquina local - image: hashicorp/terraform:0.12.17 steps: - checkout - run: name: Create Service Account key file from environment variable working_directory: terraform command: echo ${TF_SERVICE_ACCOUNT_KEY} > account.json - run: name: Show Terraform version command: terraform version - run: name: Download required Terraform plugins working_directory: terraform command: terraform init - run: name: Validate Terraform configuration working_directory: terraform command: terraform validate - run: name: Create Terraform plan working_directory: terraform command: terraform plan -out /tmp/tf.plan - run: name: Run Terraform plan working_directory: terraform command: terraform apply /tmp/tf.plan k8s_deploy: docker: - image: kiwigrid/gcloud-kubectl-helm:3.0.1-272.0.0-218 steps: - checkout - run: name: Authorize gcloud on GKE working_directory: helm command: | echo ${GCLOUD_SERVICE_KEY} > gcloud-service-key.json gcloud auth activate-service-account --key-file=gcloud-service-key.json gcloud container clusters get-credentials ${GKE_CLUSTER_NAME} --zone ${GOOGLE_COMPUTE_ZONE} --project ${GOOGLE_PROJECT_ID} - run: name: Wait a little until k8s worker nodes up command: sleep 30 # It’s a place for improvement - run: name: Create IRIS namespace if it doesn't exist command: kubectl get ns iris || kubectl create ns iris - run: name: Run Helm release deployment working_directory: helm command: | helm upgrade iris-rest \ --install \ . \ --namespace iris \ --wait \ --timeout 300s \ --atomic \ --set image.repository=eu.gcr.io/${GOOGLE_PROJECT_ID}/iris-rest \ --set image.tag=${CIRCLE_SHA1} - run: name: Check Helm release status command: helm list --all-namespaces --all - run: name: Check Kubernetes resources status command: | kubectl -n iris get pods echo kubectl -n iris get servicesworkflows: main: jobs: - terraform - gcp-gcr/build-and-push-image: dockerfile: Dockerfile gcloud-service-key: GCLOUD_SERVICE_KEY google-compute-zone: GOOGLE_COMPUTE_ZONE google-project-id: GOOGLE_PROJECT_ID registry-url: eu.gcr.io image: iris-rest path: . tag: ${CIRCLE_SHA1} - k8s_deploy: requires: - terraform - gcp-gcr/build-and-push-image
Você precisará adicionar várias variáveis de ambiente ao seu projeto no lado do CircleCI:
O GCLOUD\_SERVICE\_KEY é a chave da conta de serviço CircleCI e o TF\_SERVICE\_ACCOUNT_KEY é a chave da conta de serviço Terraform. Lembre-se de que a chave da conta de serviço é todo o conteúdo do arquivo _account.json_.
A seguir, vamos enviar nossas alterações para um repositório:
$ cd $ git add .circleci/ helm/ terraform/ .gitignore$ git commit -m "Add Terraform and Helm"$ git push
O painel da IU do CircleCI deve mostrar que tudo está bem:
Terraform é uma ferramenta idempotente e se o cluster GKE estiver presente, o trabalho "terraform" não fará nada. Se o cluster não existir, ele será criado antes da implantação do Kubernetes.Por fim, vamos verificar a disponibilidade de IRIS:
$ gcloud container clusters get-credentials --zone --project
$ kubectl -n iris get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEIris-rest LoadBalancer 10.23.249.42 34.76.130.11 52773:31603/TCP 53s
$ curl -XPOST -H "Content-Type: application/json" -u _system:SYS 34.76.130.11:52773/person/ -d '{"Name":"John Dou"}'
$ curl -XGET -u _system:SYS 34.76.130.11:52773/person/all[{"Name":"John Dou"},]
Conclusão
Terraform e Helm são ferramentas DevOps padrão e devem ser perfeitamente integrados à implantação do IRIS.
Eles exigem algum aprendizado, mas depois de alguma prática, eles podem realmente economizar seu tempo e esforço.
Artigo
Henry Pereira · Ago. 10, 2021

Calma, calma, não estou incentivando uma guerra contra as máquinas no melhor estilo sci-fi para impedir ao dominação mundial do Ultron ou da Skynet.
Ainda não... ainda não 🤔
Convido você para desafiarmos as máquinas através da criação de um jogo bem simples utilizando ObjectScript com Python embarcado.
Tenho que dizer que fiquei super empolgado com a feature do Embedded Python no InterSystems IRIS, é incrível o leque de possibilidades que se abre para criar aplicações fantásticas.
Vamos construir um jogo da velha, as regras são bem simples e acredito que todos sabem como jogar.
Era o que me salvava do tédio na minha infância durante viagens longas de carro com a família, antes de crianças terem celulares ou tablets, nada como desafiar meus irmãos a jogar algumas partidas no vidro embaçado.
Então apertem o cinto e vamos lá!
## Regras
Como comentado, as regras são muito simples:
- apenas 2 jogadores por partida
- é jogado em turnos em um grid de 3x3
- o jogador humano sempre será a letra X e o computador a letra O
- os jogadores só poderão colocar as letras nos espaços vazios
- o primeiro que completar uma sequência de 3 letras iguais na horizontal, ou na vertical ou na diagonal, é o vencedor
- quando os 9 espaços estiverem ocupados será empate e o fim da partida

Todo o mecanismo e as regras escreveremos em ObjectScript, o mecanismo do jogador do computador será escrito em Python.
## Vamos colocar as mãos na massa
Controlaremos o tabuleiro em uma global, sendo que cada linha estará em um nó e cada coluna em um piece.
Nosso primeiro método é para iniciar o tabuleiro, para facilitar irei iniciar a global já com os nós (linhas A, B e C) e com os 3 pieces:
```
/// Iniciate a New Game
ClassMethod NewGame() As %Status
{
Set sc = $$$OK
Kill ^TicTacToe
Set ^TicTacToe("A") = "^^"
Set ^TicTacToe("B") = "^^"
Set ^TicTacToe("C") = "^^"
Return sc
}
```
neste momento iremos criar o método para adicionar as letras nos espaços vazios, para isto cada jogador irá passar a localização do espaço do tabuleiro.
Cada linha uma letra e coluna um número, para colocar o X no meio, por exemplo, passamos B2 e a letra X para o método.
```
ClassMethod MakeMove(move As %String, player As %String) As %Boolean
{
Set $Piece(^TicTacToe($Extract(move,1,1)),"^",$Extract(move,2,2)) = player
}
```
Vamos validar se a coordenada passada é válida, a maneira mais simples que vejo é utilizando uma expressão regular:
```
ClassMethod CheckMoveIsValid(move As %String) As %Boolean
{
Set regex = ##class(%Regex.Matcher).%New("(A|B|C){1}[0-9]{1}")
Set regex.Text = $ZCONVERT(move,"U")
Return regex.Locate()
}
```
precisamos garantir que o espaço selecionado esteja vazio
```
ClassMethod IsSpaceFree(move As %String) As %Boolean
{
Quit ($Piece(^TicTacToe($Extract(move,1,1)),"^",$Extract(move,2,2)) = "")
}
```
Nooice!
Agora vamos verificar se algum jogador venceu a partida ou se o jogo já terminou, para isto vamos criar o método CheckGameResult.
Primeiro verificamos se teve algum vencedor completando pela horizontal, usaremos uma list com as linhas e um simples $Find resolve
```
Set lines = $ListBuild("A","B","C")
// Check Horizontal
For i = 1:1:3 {
Set line = ^TicTacToe($List(lines, i))
If (($Find(line,"X^X^X")>0)||($Find(line,"O^O^O")>0)) {
Return $Piece(^TicTacToe($List(lines, i)),"^", 1)_" won"
}
}
```
Com outro for verificamos a vertical
```
For j = 1:1:3 {
If (($Piece(^TicTacToe($List(lines, 1)),"^",j)'="") &&
($Piece(^TicTacToe($List(lines, 1)),"^",j)=$Piece(^TicTacToe($List(lines, 2)),"^",j)) &&
($Piece(^TicTacToe($List(lines, 2)),"^",j)=$Piece(^TicTacToe($List(lines, 3)),"^",j))) {
Return $Piece(^TicTacToe($List(lines, 1)),"^",j)_" won"
}
}
```
para verificar a diagonal:
```
If (($Piece(^TicTacToe($List(lines, 2)),"^",2)'="") &&
(
(($Piece(^TicTacToe($List(lines, 1)),"^",1)=$Piece(^TicTacToe($List(lines, 2)),"^",2)) &&
($Piece(^TicTacToe($List(lines, 2)),"^",2)=$Piece(^TicTacToe($List(lines, 3)),"^",3)))||
(($Piece(^TicTacToe($List(lines, 1)),"^",3)=$Piece(^TicTacToe($List(lines, 2)),"^",2)) &&
($Piece(^TicTacToe($List(lines, 2)),"^",2)=$Piece(^TicTacToe($List(lines, 3)),"^",1)))
)) {
Return ..WhoWon($Piece(^TicTacToe($List(lines, 2)),"^",2))
}
```
por último, verificamos se teve um empate
```
Set gameStatus = ""
For i = 1:1:3 {
For j = 1:1:3 {
Set:($Piece(^TicTacToe($List(lines, i)),"^",j)="") gameStatus = "Not Done"
}
}
Set:(gameStatus = "") gameStatus = "Draw"
```
Great!
## É hora de criarmos a máquina!
Vamos criar o nosso adversário, precisamos criar um algoritmo capaz de calcular todos os movimentos disponíveis e usar uma métrica para saber qual é o melhor movimento.
O ideal é utilizar um algoritmo de decisão chamado de MiniMax ([Wikipedia: MiniMax](https://en.wikipedia.org/wiki/Minimax#Minimax_algorithm_with_alternate_moves))

O algoritmo MiniMax é uma regra de decisão utilizado em teoria dos jogos, teoria de decisão e inteligência artificial.
Basicamente, precisamos saber como jogar assumindo quais serão os possíveis movimentos do oponente e pegar o melhor cenário possível.
Em detalhes, pegamos o cenário atual e recursivamente verificamos o resultado do movimento de cada jogador, caso o computador ganhar a partida pontuamos com +1, caso perder então pontuamos como -1 e 0 como um empate.
Caso não for o final do jogo, abrimos outra árvore a partir do estado atual. Feito isto encontramos a jogada com o valor máximo para o computador e a mínima para o adversário.
Veja o diagrama abaixo, existem 3 movimentos disponíveis: B2, C1 e C3.
Escolhendo C1 ou C3, o oponente tem uma chance de ganhar no próximo turno, já escolhendo B2 independente do movimento do adversário ganhamos a partida.

É como ter a joia do tempo em suas mãos para encontrar a melhor linha do tempo.

Convertendo para python
```python
ClassMethod ComputerMove() As %String [ Language = python ]
{
import iris
from math import inf as infinity
computerLetter = "O"
playerLetter = "X"
def isBoardFull(board):
for i in range(0, 8):
if isSpaceFree(board, i):
return False
return True
def makeMove(board, letter, move):
board[move] = letter
def isWinner(brd, let):
# check horizontals
if ((brd[0] == brd[1] == brd[2] == let) or \
(brd[3] == brd[4] == brd[5] == let) or \
(brd[6] == brd[7] == brd[8] == let)):
return True
# check verticals
if ((brd[0] == brd[3] == brd[6] == let) or \
(brd[1] == brd[4] == brd[7] == let) or \
(brd[2] == brd[5] == brd[8] == let)):
return True
# check diagonals
if ((brd[0] == brd[4] == brd[8] == let) or \
(brd[2] == brd[4] == brd[6] == let)):
return True
return False
def isSpaceFree(board, move):
#Retorna true se o espaco solicitado esta livre no quadro
if(board[move] == ''):
return True
else:
return False
def copyGameState(board):
dupeBoard = []
for i in board:
dupeBoard.append(i)
return dupeBoard
def getBestMove(state, player):
done = "Done" if isBoardFull(state) else ""
if done == "Done" and isWinner(state, computerLetter): # If Computer won
return 1
elif done == "Done" and isWinner(state, playerLetter): # If Human won
return -1
elif done == "Done": # Draw condition
return 0
# Minimax Algorithm
moves = []
empty_cells = []
for i in range(0,9):
if state[i] == '':
empty_cells.append(i)
for empty_cell in empty_cells:
move = {}
move['index'] = empty_cell
new_state = copyGameState(state)
makeMove(new_state, player, empty_cell)
if player == computerLetter:
result = getBestMove(new_state, playerLetter)
move['score'] = result
else:
result = getBestMove(new_state, computerLetter)
move['score'] = result
moves.append(move)
# Find best move
best_move = None
if player == computerLetter:
best = -infinity
for move in moves:
if move['score'] > best:
best = move['score']
best_move = move['index']
else:
best = infinity
for move in moves:
if move['score'] < best:
best = move['score']
best_move = move['index']
return best_move
lines = ['A', 'B', 'C']
game = []
current_game_state = iris.gref("^TicTacToe")
for line in lines:
for cell in current_game_state[line].split("^"):
game.append(cell)
cellNumber = getBestMove(game, computerLetter)
next_move = lines[int(cellNumber/3)]+ str(int(cellNumber%3)+1)
return next_move
}
```
Primeiro converto a global em um array simples, ignorando colunas e linhas deixando flat para facilitar.
A cada movimento analisado chamamos o método copyGameState, que como o nome diz, copia o estado do jogo naquele momento, onde aplicamos o MinMax.
O método getBestMove que será chamado recursivamente até finalizar o jogo encontrando um vencedor ou o empate.
Primeiro os espaços vazios são mapeados e verificamos o resultado de cada movimento alternando entre os jogadores.
Os resultados são armazenados em move['score'] para depois de verificar todas as possibilidades encontrar o melhor movimento.
Espero que você tenha se divertido, é possível melhorar a inteligência utilizando algoritmos como Alpha-Beta Pruning ([Wikipedia: AlphaBeta Pruning](https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning)) ou redes neurais, só cuidado para não dar vida a Skynet.

Fique livre para deixar comentários ou perguntas
That's all folks
Código completo:
[InterSystems Iris versão 2021.1.0PYTHON](https://www.notion.so/5be7e2147955bec0f623b718cfd83a9d)
Artigo
Henrique Dias · Nov. 25, 2020
Fala galera! Tudo bem?
Quando @Evgeny.Shvarov anunciou o primeiro InterSystems IRIS Programming Contest, Comecei a pensar em algumas ideias.
Coloquei tudo junto nesse aplicativo e o isc-utils é sobre isso:
Conversões
Temperatura
Distância
Câmbio de Moedas
Clima
Conversão de Escala de Temperatura
IRISAPP>write ##class(diashenrique.Utils.Temperature).CelsiusToFahrenheit(28)
82.4
IRISAPP>write ##class(diashenrique.Utils.Temperature).CelsiusToKelvin(28)
301.15
IRISAPP>write ##class(diashenrique.Utils.Temperature).FahrenheitToCelsius(82.4)
28
IRISAPP>write ##class(diashenrique.Utils.Temperature).FahrenheitToKelvin(82.4)
301.15
IRISAPP>write ##class(diashenrique.Utils.Temperature).KelvinToCelsius(301.15)
28
IRISAPP>write ##class(diashenrique.Utils.Temperature).KelvinToFahrenheit(301.15)
82.37
Conversão de Escala de Distância
IRISAPP>write ##class(diashenrique.Utils.Length).KmToMiles(120)
74.58
IRISAPP>write ##class(diashenrique.Utils.Length).MilesToKm(74.58)
120
Taxa de Câmbio
IRISAPP>do ##class(diashenrique.Utils.ExchangeRate).Latest(1,"USD","ALL")
Date: 2020-03-18
Conversion of 1 USD
GBP Pound sterling 0.843
HKD Hong Kong dollar 7.766
IDR Indonesian rupiah 15449.552
ILS Israeli shekel 3.810
DKK Danish krone 6.835
INR Indian rupee 74.205
CHF Swiss franc 0.965
MXN Mexican peso 23.963
CZK Czech koruna 24.834
SGD Singapore dollar 1.441
THB Thai baht 32.420
HRK Croatian kuna 6.945
EUR Euro 0.915
MYR Malaysian ringgit 4.371
NOK Norwegian krone 10.701
CNY Chinese yuan renminbi 7.035
BGN Bulgarian lev 1.789
PHP Philippine peso 51.620
PLN Polish zloty 4.117
ZAR South African rand 16.977
CAD Canadian dollar 1.440
ISK Icelandic krona 139.656
BRL Brazilian real 5.134
RON Romanian leu 4.431
NZD New Zealand dollar 1.713
TRY Turkish lira 6.447
JPY Japanese yen 107.719
RUB Russian rouble 79.656
KRW South Korean won 1260.106
USD US dollar 1.000
AUD Australian dollar 1.698
HUF Hungarian forint 321.365
SEK Swedish krona 10.081
Parâmetros:
Quantia
Moeda base. Ex.: JPY
Taxas de Câmbio
ALL - para todas as moedas disponíveis na API
Taxas de câmbio de moedas específicas. Ex.: "BRL,AUD,CAD,RUB"
Por data
IRISAPP>do ##class(diashenrique.Utils.ExchangeRate).ByDate("2020-01-01",1,"USD","BRL,JPY,AUD,CAD")
Date: 2020-01-01
Conversion of 1 USD
JPY Japanese yen 108.545
AUD Australian dollar 1.424
CAD Canadian dollar 1.299
BRL Brazilian real 4.020
Parâmetros:
Data = YYYY-MM-DD
Quantia
Moeda base. Ex.: BRL
Taxas de Câmbio
ALL - para todas as moedas disponíveis na API
Taxas de câmbio de moedas específicas. Ex.: "BRL,AUD,CAD,RUB"
Existe uma global ^defaultCurrency, que mantém suas opções de moedas preferidas.
Sendo assim, você pode chamar o ClassMethod Latest, sem nenhum parâmetro
IRISAPP>do ##class(diashenrique.Utils.ExchangeRate).Latest()
Default Base Currency:
Apenas informe a moeda base de sua preferência:
IRISAPP>do ##class(diashenrique.Utils.ExchangeRate).Latest()
Default Base Currency: USD
Date: 2020-03-18
Conversion of 1 USD
GBP Pound sterling 0.843
HKD Hong Kong dollar 7.766
IDR Indonesian rupiah 15449.552
ILS Israeli shekel 3.810
DKK Danish krone 6.835
INR Indian rupee 74.205
CHF Swiss franc 0.965
MXN Mexican peso 23.963
CZK Czech koruna 24.834
SGD Singapore dollar 1.441
THB Thai baht 32.420
HRK Croatian kuna 6.945
EUR Euro 0.915
MYR Malaysian ringgit 4.371
NOK Norwegian krone 10.701
CNY Chinese yuan renminbi 7.035
BGN Bulgarian lev 1.789
PHP Philippine peso 51.620
PLN Polish zloty 4.117
ZAR South African rand 16.977
CAD Canadian dollar 1.440
ISK Icelandic krona 139.656
BRL Brazilian real 5.134
RON Romanian leu 4.431
NZD New Zealand dollar 1.713
TRY Turkish lira 6.447
JPY Japanese yen 107.719
RUB Russian rouble 79.656
KRW South Korean won 1260.106
USD US dollar 1.000
AUD Australian dollar 1.698
HUF Hungarian forint 321.365
SEK Swedish krona 10.081
Clima
IRISAPP>do ##class(diashenrique.Utils.Weather).GetWeather()
Default City:
Default Country:
Default Termo Scale(C,F,K):
O ClassMethod GetWeather, tem opções de preferência também. Para possibilidades padrão, temos:
Cidade
País
Escala de Temperatura (Celsius, Fahrenheit ou Kelvin)
IRISAPP>do ##class(diashenrique.Utils.Weather).GetWeather()
Default City: Boston
Default Country: USA
Default Termo Scale(C,F,K): F
City: Boston | Country: USA
Temperature: 53.46 °F
Real Feel: 46.02 °F
Condition: Clear
Mas, você pode consultar qualquer outra cidade apenas passando os parâmetros:
IRISAPP>do ##class(diashenrique.Utils.Weather).GetWeather("Sao Paulo","Brazil","C")
City: Sao Paulo | Country: Brazil
Temperature: 27.55 °C
Real Feel: 28.4 °C
Condition: Rain
Depois de definir seus parâmetros padrão, eles se tornam opcionais:
IRISAPP>do ##class(diashenrique.Utils.Weather).GetWeather("Sao Paulo","Brazil")
City: Sao Paulo | Country: Brazil
Temperature: 82.17 °F
Real Feel: 83.95 °F
Condition: Rain