Quantcast
Channel: dbi Blog
Viewing all 2832 articles
Browse latest View live

Deploying a Kubernetes cluster on EC2 with Rancher

$
0
0

Once Rancher is up and running, it makes the deployment and management of Kubernetes clusters quite easy. In this post we’ll deploy a brand new cluster on top of EC2. If you want to have a simple and quick Rancher playground you can follow this post, which will give you a Rancher setup on SLES 15. If you want to have a more production like Rancher setup, you can follow these posts: Rancher, up and running, on EC2 – 1 – One node, Rancher, up and running, on EC2 – 2 – Three nodes and Rancher, up and running, on EC2 – 3 – Rancher setup. Those will give you a three nodes cluster on top of EC2, running on Debian.

Before you start with this, make sure, that you meet these requirements:

  • The host on which you run Rancher needs to communicate with all instances you deploy on EC2, in both directions. If you have Rancher running locally this will only work if the EC2 instances will be able to reach your local Rancher installation.
  • You need to setup the correct IAM policies and groups. If you don’t get this right you will not be able to deploy the cluster.

Because this is the most important point, lets start with the IAM policies. I’ve created three of them:

  • dwe-rancher-controlpane-policy: This is the policy that will be used for the control pane
  • dwe-rancher-etcd-worker-policy: This is the policy that will be used for the etcd and worker nodes
  • dwe-rancher-passrole-policy: This is the policy that will be attached to the AWS user that will be registered in Rancher with the cloud credentials

Here is the dwe-rancher-controlpane-policy (replace [YOUR_AWS_ACCOUNT_ID] with your AWS account ID):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:AttachVolume",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:DescribeInstances",
                "autoscaling:DescribeLaunchConfigurations",
                "ec2:DescribeRegions",
                "elasticloadbalancing:DescribeLoadBalancerPolicyTypes",
                "elasticloadbalancing:SetWebAcl",
                "elasticloadbalancing:DescribeLoadBalancers",
                "ec2:DeleteVolume",
                "elasticloadbalancing:DescribeListeners",
                "autoscaling:DescribeAutoScalingGroups",
                "ec2:CreateRoute",
                "ec2:CreateSecurityGroup",
                "ec2:DescribeVolumes",
                "elasticloadbalancing:DescribeLoadBalancerPolicies",
                "kms:DescribeKey",
                "elasticloadbalancing:DescribeListenerCertificates",
                "elasticloadbalancing:DescribeInstanceHealth",
                "ec2:ModifyInstanceAttribute",
                "ec2:DescribeRouteTables",
                "elasticloadbalancing:DescribeSSLPolicies",
                "ec2:DetachVolume",
                "ec2:ModifyVolume",
                "ec2:CreateTags",
                "autoscaling:DescribeTags",
                "ec2:DeleteRoute",
                "elasticloadbalancing:*",
                "ec2:DescribeSecurityGroups",
                "ec2:CreateVolume",
                "elasticloadbalancing:DescribeLoadBalancerAttributes",
                "ec2:RevokeSecurityGroupIngress",
                "iam:CreateServiceLinkedRole",
                "elasticloadbalancing:DescribeTargetGroupAttributes",
                "ec2:DescribeVpcs",
                "elasticloadbalancing:DescribeAccountLimits",
                "ec2:DeleteSecurityGroup",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeRules",
                "ec2:DescribeSubnets"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/*"
        },
        {
            "Sid": "VisualEditor3",
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": [
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:targetgroup/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener-rule/app/*/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener-rule/net/*/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener/net/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener/app/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/net/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/app/*/*"
            ]
        },
        {
            "Sid": "VisualEditor4",
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": [
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:targetgroup/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener-rule/app/*/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener-rule/net/*/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener/net/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener/app/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/net/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/app/*/*"
            ]
        },
        {
            "Sid": "VisualEditor5",
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": [
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/app/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:loadbalancer/net/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:targetgroup/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener-rule/app/*/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener-rule/net/*/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener/net/*/*/*",
                "arn:aws:elasticloadbalancing:*:[YOUR_AWS_ACCOUNT_ID]:listener/app/*/*/*"
            ]
        }
    ]
}

Here is the dwe-rancher-etcd-worker-policy (replace [YOUR_AWS_ACCOUNT_ID] with your AWS account ID):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ec2:*",
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "secretsmanager:*",
            "Resource": "arn:aws:secretsmanager:*:[YOUR_AWS_ACCOUNT_ID]:secret:*"
        }
    ]
}

Finally, here is the content of dwe-rancher-passrole-policy (here you need to reference the other two policies):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:ModifyInstanceMetadataOptions",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:Describe*",
                "ec2:ImportKeyPair",
                "ec2:CreateKeyPair",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "eks:*",
                "ec2:DeleteKeyPair"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ec2:RunInstances",
            "Resource": [
                "arn:aws:ec2:eu-central-1::image/ami-*",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:security-group/*",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:subnet/*",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:network-interface/*",
                "arn:aws:iam::[YOUR_AWS_ACCOUNT_ID]:role/dwe-rancher-controlpane-role",
                "arn:aws:iam::[YOUR_AWS_ACCOUNT_ID]:role/dwe-rancher-etcd-worker-role",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:instance/*",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:volume/*",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:placement-group/*",
                "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:key-pair/*"
            ]
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "ec2:RebootInstances",
                "ec2:TerminateInstances",
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "arn:aws:ec2:eu-central-1:[YOUR_AWS_ACCOUNT_ID]:instance/*"
        },
        {
            "Sid": "VisualEditor3",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": [
                "arn:aws:iam::[YOUR_AWS_ACCOUNT_ID]:role/dwe-rancher-controlpane-role",
                "arn:aws:iam::[YOUR_AWS_ACCOUNT_ID]:role/dwe-rancher-etcd-worker-role"
            ]
        }
    ]
}

Once you have that ready, create two IAM roles with the same name as the policies you created above. This is required, because you need to specify those later when you setup the node templates in Rancher:

The final step for the permissions in AWS is to assign the last policy as a permission to the AWS user you will be using for deploying the cluster:

As Rancher needs to communicate with AWS you need to create your credentials:

Here you need to provide your AWS access key and the secret:

If you do not know, how to get the access key and the secret, you can generate that in the IAM console for your AWS user:

The next piece to get in place are the “node templates”. These templates describe which EC2 instance types you want to use, which AWS region to go for and a few other properties that describe your nodes:

Here you need to reference the cloud credentials you’ve created above:

Your VPC and Subnets are should be configured in AWS as you need to specify those in the next step:

You can either let Rancher create a security group for you, or you can use an existing one:

Be careful if you let Rancher do it, as the security group that gets created in the background is quite open and you should _not_ do it like this:

This is the most important section: The AMI ID you see, is the latest Ubuntu 20.04 AMI. The user for that AMI is “ubuntu”. If you want to go with a Debian, CentOS or whatever AMI you need to adjust those (The user for Debian would be “admin”, for CentOS it would be “centos” ). The “IAM instance profile name” is the role you created above, and this is important. Here you see “dwe-rancher-controlpane-role” because this will be the node template for the controller pane:

Give your template a name and stick to the dedaults for the rest:

Create a nother node template in exactly the same way as the last one. The only difference is the name and the “IAM instance profile name”, which is now the one for the etcd and the worker nodes:

Now we are ready to deploy a brand new Kubernetes cluster on top of EC2:

Here you reference the node templates. Make sure you use the control pane template for the control pane, and the other templates for etcd and worker nodes:

Go with the default and select “AWS” as cloud provider:

Before you press “Create”, it is a good idea to log into your Rancher host and tail the logs of the Rancher container. If anything goes wrong it shows up there:

admin@ip-10-0-1-69:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                      NAMES
82a08d1aba46        rancher/rancher     "entrypoint.sh"     3 hours ago         Up 3 hours          0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   infallible_hugle
admin@ip-10-0-1-69:~$ docker logs 82a08d1aba46 -f
2021/03/12 06:24:20 [INFO] Rancher version v2.5.6 (65f7c8442) is starting
2021/03/12 06:24:20 [INFO] Rancher arguments {ACMEDomains:[] AddLocal:true Embedded:false BindHost: HTTPListenPort:80 HTTPSListenPort:443 K8sMode:auto Debug:false Trace:false NoCACerts:false AuditLogPath:/var/log/auditlog/rancher-api-audit.log AuditLogMaxage:10 AuditLogMaxsize:100 AuditLogMaxbackup:10 AuditLevel:0 Agent:false Features:}
2021/03/12 06:24:20 [INFO] Listening on /tmp/log.sock

Once you started the cluster creation, you can also monitor the EC2 console and watch the EC instances coming up:


Have a look at the main cluster screen and you should see the status changing several times until the cluster is fully provisioned:








The cluster is fully ready and you can drill into the cluster section:

Cet article Deploying a Kubernetes cluster on EC2 with Rancher est apparu en premier sur Blog dbi services.


ODA: Move a DB to ASM after migrating from 11g to 19c

$
0
0

Introduction

Modern Oracle Database Appliances still support 11gR2, but there are several drawbacks using this old version:

  • if you want/need support for your databases, you should pay for Market Driven Support (11gR2 is even no more under extended support – it ended at the end of 2020)
  • some ODA features will not work with 11gR2 (odacli Data Guard feature for example)
  • the use of ACFS filesystems is mandatory

As I already told you, it’s more than time to migrate to 19c. Once done, you could get back to Premier Support and benefit for all ODA features dedicated to most recent versions. But what about ACFS filesystems?

ACFS is supported for all versions

ACFS is supported for all database versions, and migrating from 11gR2 to 19c will keep ACFS filesystems without any problem. Some of your databases can be using ASM, and other ACFS, both can live together.

Should I migrate to ASM?

If you are using ASM for your newest databases, it could be nice to keep only ASM and get rid of ACFS. ASM is fine as you don’t need to split your disks in numerous filesystems, so you optimize the disk usage (as disks on ODA are expensive).

How do I migrate from ACFS to ASM?

There are multiple methods to do this migration.

One of these methods is to use RMAN to move the datafiles with a BACKUP AS COPY DATABASE FORMAT ‘+DATA’ and then do a ‘SWITCHOVER DATABASE TO COPY’. It’s easy but it will only move datafiles, other files will need a manual move (redologs, controlfiles, archivelogs). Furthermore, your database will keep its ACFS volumes, and you should remove them manually. There is no way to detach ACFS volume from a database with odacli, unfortunately (for now).

Another way to achieve this goal is to use odacli backup and restore features. Because odacli will pack everything to keep your ODA registry clean. And that’s everyone’s wish. A clean ODA repository is highly recommended.

Where do I would put the temporary backup?

For sure, you can backup your database to an external volume, but as it’s for restoring on the same ODA, you’d better do the backup locally, it means in the FRA. It will minimize the time needed for the operation (NVMe disk to NVMe disk).

How to do the database migration from 11gR2 to 19c?

Let’s first create an 11gR2 database. Remember that you do not give the version of the database in the create-database command, you just provide the Database Home associated to 11gR2 binaries (-dh):

odacli create-database -dh e5db1546-2a78-416c-a70b-ea6a1ff022cd -u DEMO_767P -n DEMO -r ACFS -cs WE8ISO8859P15 -no-c -no-co -s odb2

Once database is created, let’s upgrade it to 19c, the -to is the target Database Home, the one running 19c:

odacli upgrade-database -i 371f1b1e-f1fe-4910-be10-e395d2c4ef67 -to fe4c4883-d8f6-4dad-97e8-ce04870fd9db

It will submit a job and would last from minutes to hours.

At the end of the migration, I’m used to change the compatible version:

sqlplus / as sysdba
alter system set compatible='19.0.0.0' scope=spfile;
shutdown immediate;
startup;
exit;

Create a backup configuration and link it to the database

To be able to restore with odacli, a backup needs to be made by odacli. It supposes to create a backupconfig first:

odacli create-backupconfig -n Local -w 1 -d Disk

Now let’s associate this backupconfig to the database:

odacli modify-database -in DEMO -bin Local

Create the backup and save the report to a json file

Now let’s take a first backup:

odacli create-backup --backupType Regular-L0 -in DEMO -ka

List the backupreports to identify the backup task, and then describe the backupreport to a json file:

odacli list-backupreports 
odacli describe-backupreport -i 0bc90ed3-1fe0-472e-8191-a587c0b44de0 > /tmp/br_DEMO_`date +"%Y%m%d_%H%M%S"`.json

Delete the source database

This is only mandatory if you want to keep the database name, and I would like to in this DEMO:

odacli list-databases

ID                                       DB Name    DB Type  DB Version           CDB        Class    Shape    Storage    Status        DbHomeID
---------------------------------------- ---------- -------- -------------------- ---------- -------- -------- ---------- ------------ ----------------------------------------
8ad09a72-fbcf-410f-854b-cd8c945fb3de     DBTEST     Si       19.9.0.0.201020      false      Oltp     Odb1s    Asm        Configured   fe4c4883-d8f6-4dad-97e8-ce04870fd9db
72723b58-bc91-4a04-9122-3b1e214bede9     MIG        Si       19.9.0.0.201020      false      Oltp     Odb2     Acfs       Configured   fe4c4883-d8f6-4dad-97e8-ce04870fd9db
13b24dc6-f773-403b-a6e7-5b78d6b4c8b2     DBASM      Si       19.9.0.0.201020      false      Oltp     Odb2     Asm        Configured   fe4c4883-d8f6-4dad-97e8-ce04870fd9db
371f1b1e-f1fe-4910-be10-e395d2c4ef67     DEMO       Si       19.9.0.0.201020      false      Oltp     Odb2     Acfs       Configured   fe4c4883-d8f6-4dad-97e8-ce04870fd9db

odacli delete-database -i 371f1b1e-f1fe-4910-be10-e395d2c4ef67

Restore the database

odacli is able to restore a database to a different filesystem, ASM for example:

odacli irestore-database -dh fe4c4883-d8f6-4dad-97e8-ce04870fd9db -r /tmp/br_DEMO_20201126_112503.json -dr ASM -bl /u03/app/oracle/fast_recovery_area/DEMO_767P/backupset/2020_11_26/

Describe the job with odacli to have a status of the task:

Job details
----------------------------------------------------------------
                     ID:  5456a45a-ee7f-49eb-a6ae-3d4ffc9b5b42
            Description:  Database service recovery with db name: DEMO
                 Status:  Success
                Created:  November 26, 2020 11:30:56 AM CET
                Message:

Task Name                                Start Time                          End Time                            Status
---------------------------------------- ----------------------------------- ----------------------------------- ----------
Check if cluster ware is running         November 26, 2020 11:30:56 AM CET   November 26, 2020 11:30:56 AM CET   Success
Creating DbStorage for DbRestore         November 26, 2020 11:30:56 AM CET   November 26, 2020 11:30:57 AM CET   Success
Validating DiskSpace for DATA            November 26, 2020 11:30:56 AM CET   November 26, 2020 11:30:57 AM CET   Success
Generating SSH key                       November 26, 2020 11:30:57 AM CET   November 26, 2020 11:30:57 AM CET   Success
SSH key                                  November 26, 2020 11:30:57 AM CET   November 26, 2020 11:30:57 AM CET   Success
SSH key scan                             November 26, 2020 11:30:57 AM CET   November 26, 2020 11:30:57 AM CET   Success
Create TDE And Audit Dir Locations       November 26, 2020 11:30:57 AM CET   November 26, 2020 11:30:57 AM CET   Success
Create pfile for Auxiliary Instance      November 26, 2020 11:30:57 AM CET   November 26, 2020 11:30:57 AM CET   Success
Deleting FRA                             November 26, 2020 11:30:57 AM CET   November 26, 2020 11:30:58 AM CET   Success
Rman duplicate                           November 26, 2020 11:30:58 AM CET   November 26, 2020 11:35:24 AM CET   Success
Delete RECO FileGroup DEMO_767P          November 26, 2020 11:35:24 AM CET   November 26, 2020 11:35:25 AM CET   Success
Create RECO FileGroup DEMO_767P          November 26, 2020 11:35:25 AM CET   November 26, 2020 11:35:25 AM CET   Success
Delete RECO FileGroup DEMO_767P_9999     November 26, 2020 11:35:25 AM CET   November 26, 2020 11:35:25 AM CET   Success
Creating pfile from spfile               November 26, 2020 11:35:25 AM CET   November 26, 2020 11:35:25 AM CET   Success
Set PFile Ownership                      November 26, 2020 11:35:25 AM CET   November 26, 2020 11:35:26 AM CET   Success
Customize Db Parameters                  November 26, 2020 11:35:26 AM CET   November 26, 2020 11:35:27 AM CET   Success
Shutdown And Start database              November 26, 2020 11:35:27 AM CET   November 26, 2020 11:36:49 AM CET   Success
Create spfile for restore db             November 26, 2020 11:36:49 AM CET   November 26, 2020 11:36:49 AM CET   Success
Set PFile Ownership                      November 26, 2020 11:36:49 AM CET   November 26, 2020 11:36:49 AM CET   Success
Shutdown And Mount database              November 26, 2020 11:36:49 AM CET   November 26, 2020 11:37:32 AM CET   Success
Re-Create control file                   November 26, 2020 11:37:32 AM CET   November 26, 2020 11:38:12 AM CET   Success
Removing Disabled Redo Threads           November 26, 2020 11:38:12 AM CET   November 26, 2020 11:38:13 AM CET   Success
Updating DB attributes                   November 26, 2020 11:38:13 AM CET   November 26, 2020 11:38:13 AM CET   Success
Enable block change tracking             November 26, 2020 11:38:13 AM CET   November 26, 2020 11:38:16 AM CET   Success
Register Database taskflow               November 26, 2020 11:38:17 AM CET   November 26, 2020 11:42:38 AM CET   Success
Create SPFile in shared loc              November 26, 2020 11:38:17 AM CET   November 26, 2020 11:38:21 AM CET   Success
Delete Local Spfile                      November 26, 2020 11:38:21 AM CET   November 26, 2020 11:38:21 AM CET   Success
Register DB with clusterware             November 26, 2020 11:38:21 AM CET   November 26, 2020 11:39:40 AM CET   Success
Add Startup Trigger to Open all PDBS     November 26, 2020 11:39:40 AM CET   November 26, 2020 11:39:40 AM CET   Success
Set SysPassword and Create PwFile        November 26, 2020 11:39:40 AM CET   November 26, 2020 11:39:42 AM CET   Success
Creating pfile                           November 26, 2020 11:39:42 AM CET   November 26, 2020 11:39:43 AM CET   Success
Updating db env                          November 26, 2020 11:39:43 AM CET   November 26, 2020 11:39:44 AM CET   Success
Enable DbSizing Template                 November 26, 2020 11:39:44 AM CET   November 26, 2020 11:41:00 AM CET   Success
Update Database Global Name              November 26, 2020 11:41:00 AM CET   November 26, 2020 11:41:01 AM CET   Success
Create tns entry                         November 26, 2020 11:41:01 AM CET   November 26, 2020 11:41:02 AM CET   Success
Running datapatch                        November 26, 2020 11:41:02 AM CET   November 26, 2020 11:41:11 AM CET   Success
Set CPU pool                             November 26, 2020 11:41:11 AM CET   November 26, 2020 11:41:11 AM CET   Success
Reset Associated Networks                November 26, 2020 11:42:38 AM CET   November 26, 2020 11:42:41 AM CET   Success
Copy Pwfile to Shared Storage            November 26, 2020 11:42:41 AM CET   November 26, 2020 11:42:44 AM CET   Success

That’s it, our database is now using ASM like the other databases. You can control with another:

odacli list-database

Conclusion

This is the cleanest way to move a database from ACFS to ASM or vice-versa. Please make sure your backup is OK before deleting the database. Be carreful if your database is the latest one to use ACFS, because your recovery area ACFS volume could be removed after the source database deletion. If you want to make sure that your backup will persist, do it on a nfs share or restore the database with a new name.

Cet article ODA: Move a DB to ASM after migrating from 11g to 19c est apparu en premier sur Blog dbi services.

Getting started with the Zalando Operator for PostgreSQL

$
0
0

In the previous posts (Rancher, up and running, on EC2 – 1 – One node, Rancher, up and running, on EC2 – 2 – Three nodes, Rancher, up and running, on EC2 – 3 – Rancher setup, Rancher on SLES 15 as a demo environment, and Deploying a Kubernetes cluster on EC2 with Rancher) it was all about deploying a Kubernetes cluster and getting Rancher up and running. In this post we’ll use one of those systems to deploy a PostgreSQL cluster. I’ll be using my one node Rancher demo environment for this, but you can of course also use a cluster as described in the posts above. Zalando does not only provide Patroni but also provides a PostgreSQL operator for Kubernetes, and we’ll be using this one for this demo.

Before we start with the PostgreSQL operator we need to prepare the system. PostgreSQL needs to safely store it’s data somewhere, and for being able to do that we need persistent storage. We could do all the following steps using the command line, but we’ll use the Rancher UI for this. The easiest way to do this is by using the “Cluster Explorer”

On the left side you’ll find the Persistent Volumes:


As there is no storage system attached to the demo environment, we’ll be using the “Host Path” plugin for my persistent volumes, and I’ll be creating two of them:



We can also use the command line for checking the persistent volumes we’ve just created:

rancher@debian10ranger:~$ kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv-postgresql-1   10Gi       RWO            Retain           Available                                   8m50s
pv-postgresql-2   10Gi       RWO            Retain           Available                                   7m32s

That’s it for the preparation and we are ready to clone the PostgreSQL operator repository:

rancher@debian10ranger:~$ git clone https://github.com/zalando/postgres-operator.git
Cloning into 'postgres-operator'...
remote: Enumerating objects: 273, done.
remote: Counting objects: 100% (273/273), done.
remote: Compressing objects: 100% (178/178), done.
remote: Total 20089 (delta 176), reused 173 (delta 93), pack-reused 19816
Receiving objects: 100% (20089/20089), 7.75 MiB | 13.29 MiB/s, done.
Resolving deltas: 100% (14256/14256), done.
rancher@debian10ranger:~$ cd postgres-operator/

The first step is to create the config map (have a look at it to get an idea of all the parameters and values):

rancher@debian10ranger:~/postgres-operator$ kubectl create -f manifests/configmap.yaml 
configmap/postgres-operator created

This was the way to do it on the command line, the same is possible with the Rancher UI as well:

Next is the RBAC (role based access control) stuff (I’ve done that already before and did not do a proper cleanup, that’s why the postgres-operator service account already exists):

rancher@debian10ranger:~/postgres-operator$ kubectl create -f manifests/operator-service-account-rbac.yaml
clusterrole.rbac.authorization.k8s.io/postgres-operator created
clusterrolebinding.rbac.authorization.k8s.io/postgres-operator created
clusterrole.rbac.authorization.k8s.io/postgres-pod created
Error from server (AlreadyExists): error when creating "manifests/operator-service-account-rbac.yaml": serviceaccounts "postgres-operator" already exists

The PostgreSQL operator itself:

rancher@debian10ranger:~/postgres-operator$ kubectl create -f manifests/postgres-operator.yaml
deployment.apps/postgres-operator created

… and finally the service:

rancher@debian10ranger:~/postgres-operator$ kubectl create -f manifests/api-service.yaml
service/postgres-operator created

A few seconds later the operator pod should be running:

rancher@debian10ranger:~/postgres-operator$ cd ..
rancher@debian10ranger:~$ kubectl get pod -l name=postgres-operator
NAME                                 READY   STATUS    RESTARTS   AGE
postgres-operator-6775b6dcf9-cpp86   1/1     Running   0          2m42s

For getting the graphical user interface for the PostgreSQL operator install the UI components:

rancher@debian10ranger:~$ kubectl apply -f postgres-operator/ui/manifests/
deployment.apps/postgres-operator-ui created
ingress.networking.k8s.io/postgres-operator-ui created
service/postgres-operator-ui created
serviceaccount/postgres-operator-ui unchanged
clusterrole.rbac.authorization.k8s.io/postgres-operator-ui created
clusterrolebinding.rbac.authorization.k8s.io/postgres-operator-ui created
error: unable to recognize "postgres-operator/ui/manifests/kustomization.yaml": no matches for kind "Kustomization" in version "kustomize.config.k8s.io/v1beta1"

Same procedure as before, check if the pod is running:

rancher@debian10ranger:~$ kubectl get pod -l name=postgres-operator-ui
NAME                                    READY   STATUS    RESTARTS   AGE
postgres-operator-ui-5478dcfd7b-r2rzr   1/1     Running   0          74s

Check the Rancher UI for the service that got created (or use the command line to get that information):

… and access the Operator user interface with your browser:

Using this interface, a two node PostgreSQL cluster can easily be created:


The new cluster is ready:

rancher@debian10ranger:~$ kubectl get pods 
NAME                                    READY   STATUS    RESTARTS   AGE
acid-my-demo-cluster-0                  1/1     Running   0          4m58s
acid-my-demo-cluster-1                  1/1     Running   0          4m54s
postgres-operator-6775b6dcf9-cpp86      1/1     Running   0          24m
postgres-operator-ui-5478dcfd7b-r2rzr   1/1     Running   0          19m

Quite easy to get that up and running in a demo environment.

Cet article Getting started with the Zalando Operator for PostgreSQL est apparu en premier sur Blog dbi services.

Linux perf-top basics: understand the %

$
0
0

By Franck Pachot

.
Linux kernel has a powerful instrumentation that can be accessed easily. When you want to drill down into your program functions to understand their CPU usage, “perf” is the easiest. It can attach to the processes, sample the CPU cycles, get the symbol name, or even the call stack. And display an histogram of sample counts. This provides an easy profiling tool to understand in which function your program spends its CPU time, so that you know where an improvement can optimize the overall resource usage. But I’m seeing that people may be intimidated by this kind of tools and doesn’t know how to interpret the percentages.

The best way to confirm your understanding is running a small example where you know the behavior, and look at which numbers the tool provides.


f1(int n) {
  int i; for (i = 0; i <= 5; i++) {}; return i;
};
f2(int n) {
  int i; for (i = 0; i <= 5; i++) {}; return i;
};
main() {
  int r; 
  int i; for (i = 0;; i++) {
    (i % 3 == 0) ? f1(i): f2(i);
  };
  return r;
}

I have two functions f1() and f2() which do exactly the same thing. I just want to have different names. And the main() function loops and calls the f1() function 1/3rd of times and f2() 2/3rd of times, thanks to the (i % 3) condition on modulo 3


perf top -F100 -d10 -p$(pgrep -d, a.out)

You can simply run `perf top` but here I explicitly reduced the sample frequency to 100Hz (safer to reduce the overhead of sampling) and the display refresh frequency to 10 seconds because it is simple to take consistent screenshots. I measure only my program by getting the PID through `pgrep -d, a.out`. I display only the userspace symbols by hiding the kernel ones (-K). There are many possibilities to target specific processes in `perf` as well in `pgrep`but that’s not the goal of this post. The man pages are clear about that.


Samples: 4K of event 'cycles', 100 Hz, Event count (approx.): 69818773752 lost: 0/0 drop: 0/0
Overhead  Share  Symbol
  59.86%  a.out  [.] f2
  31.10%  a.out  [.] f1
   9.04%  a.out  [.] main

The “Shared Object” is my program (a.out) or a library that is called, or the kernel. Here I didn’t specify the process. Symbols are the C functions. And the “Overhead” percentage sums to 100% and this is where it is important to filter what you sample so that it is easy to profile it as a ratio of the total samples for this target.

This is the default option, showing the symbols but not the call stack, the percentages are the time spend in the function. My program spends only 9.04% of its CPU time in main() because very few is done there. When main() calls a function, the samples are accounted for the function but not for main(). On the remaining time (91%) one third of the time is spent in f1() and two third in f2().

This may already be sufficient to know where to investigate for optimizing the code. For example, if you divide by two the work done in f2() you know that this will reduce by 30% the CPU usage for your program (the math is: 50% x 59.86%)

call-graph

Reducing the resources used by a function is one possibility. But the best optimization would be to avoid calling it too often. And I have no information to help me about that. Nothing here tells me that f1() is called from main(). It could have been called from f2(). Or from both. When troubleshooting a program execution, knowing the function is not enough, we need to see the whole call stack.


perf top -F100 -d10 -p$(pgrep a.out) -K -g

I’ve added -g here to record the call stack (not only in which function we are when sampling, but also where the functions come from). There are different modes that you can choose with –call-graph but I’m using the default here.


Samples: 3K of event 'cycles', 100 Hz, Event count (approx.): 37649486188 lost: 0/0 drop: 0/0
  Children      Self  Shared Objec  Symbol
+   95.61%     8.19%  a.out         [.] main
+   60.64%    60.64%  a.out         [.] f2
+   31.17%    31.17%  a.out         [.] f1
+   15.55%     0.00%  libc-2.17.so  [.] __libc_start_main

Here the “Self” column is similar to what I had without “-g”: this is the percentage of CPU cycles spent in each function. But the “Children” column adds the time spent in all called functions below it. Not only the immediate children, but all descendants. For leaves of the call graph, functions not calling anything else, the value of Self and Children is equal. But for main() it adds the time spent in f1()<-main() and f2()<-main(). You read the first line as: 95.61% of time is spent in the call to main() and only 8.19% is on main() instructions because it calls other functions most of the time. Note that you cad add “Self” to cover 100% but in “Children” the children samples are accounted in many lines. The idea is to see on top the fragment of call stack that accounts for the most samples.

There’s a “+” where you can drill down. I do it for all of them here be you typically go to the one you want to analyze deeper:


-   95.61%     8.19%  a.out         [.] main     
   - 87.42% main                                 
        57.68% f2                                
        29.74% f1                                
   - 8.19% __libc_start_main                     
        main                                     
-   60.64%    60.64%  a.out         [.] f2       
   - 60.64% __libc_start_main                    
      - 57.68% main                              
           f2                                    
        2.97% f2                                 
-   31.17%    31.17%  a.out         [.] f1       
   - 31.17% __libc_start_main                    
      - 29.74% main                              
           f1                                    
        1.42% f1                                 
-   15.55%     0.00%  libc-2.17.so  [.] __libc_start_main 
   - __libc_start_main                                    
      - 14.63% main                                       
           8.96% f2                                       
           4.58% f1                      

When drilling-down the first line I can see that the difference between 95.61% in main() call and 8.19% in main() instructions is in calls to f2() and f1(). Note that you understand the reason why I’ve set –delay to 10 seconds: to be able to drill down on the same numbers before refresh in order to make this clearer in the blog post. Because with 100Hz sampling the numbers may change slightly.

callee/caller

In order to investigate on functions that are called from many places, I replace the work done in f2() by a call to f1():


f1(int n) {
  int i; for (i = 0; i <= 5; i++) {}; return i;
};
f2(int n) {
  return f1(n);
};
main() {
  int r; 
  int i; for (i = 0;; i++) {
    (i % 3 == 0) ? f1(i): f2(i);
  };
  return r;
}

Now, in addition to being called 1/3 of time from main, most of the time spent in f2() is also calling f1().


Samples: 255  of event 'cycles', 100 Hz, Event count (approx.): 5934954123 lost: 0/0 drop: 0/0
Overhead  Share  Symbol
  83.59%  a.out  [.] f1
  12.81%  a.out  [.] main
   3.60%  a.out  [.] f2

Without the call graph, the time spend in the function instructions is, proportionally to the total time, now mostly in f1() because f1() is called in all by branch condition. The part accounted in f2() is minimal.


perf record -F100 -g --delay=5 -v -p $(pgrep -d, a.out) -a sleep 30
perf report | cat

Rather than looking at it live, I record the samples (mentioning the PID but running a `sleep 30` to record for 30 seconds).


Samples: 764  of event 'cycles', Event count (approx.): 18048581591
  Children      Self  Command  Shared Object      Symbol
+  100.00%     0.00%  a.out    libc-2.17.so       [.] __libc_start_main
+   99.47%    10.87%  a.out    a.out              [.] main
-   86.22%    85.68%  a.out    a.out              [.] f1
   - 85.68% __libc_start_main
      - main
         - 57.78% f2
              f1
           27.90% f1
   - 0.54% f1
      + 0.54% apic_timer_interrupt
-   60.82%     2.77%  a.out    a.out              [.] f2
   - 58.05% f2
        f1
   - 2.77% __libc_start_main
      - 2.24% main
           f2
        0.53% f2
+    0.68%     0.00%  a.out    [kernel.kallsyms]  [k] apic_timer_interrupt
+    0.68%     0.00%  a.out    [kernel.kallsyms]  [k] smp_apic_timer_interrupt

If we look at the f1() detail we can see 27.90% with f1()<-main() which is the 1/3rd calls from there. And 57.78% with f1()<-f2()<-main() for the 2/3rd of the conditional branch. With of course some time in main() itself (10.87%) and in f2() itself (2.77%)

This is the caller breakdown which can also be displayed with `perf report –call-graph ,,,,caller`
We see that f1() is on CPU 57.78% of time through f1()<-f2()<-main() and 27.90% directly through f2()<-main()

We can also display it with breakdown on callee: by changing the call graph order as `perf report –call-graph ,,,,callee`


Samples: 764  of event 'cycles', Event count (approx.): 18048581591
  Children      Self  Command  Shared Object      Symbol
-  100.00%     0.00%  a.out    libc-2.17.so       [.] __libc_start_main
     __libc_start_main
-   99.47%    10.87%  a.out    a.out              [.] main
     main
     __libc_start_main
-   86.22%    85.68%  a.out    a.out              [.] f1
   - f1
      - 58.05% f2
           main
           __libc_start_main
      - 28.17% main
           __libc_start_main
-   60.82%     2.77%  a.out    a.out              [.] f2
   - f2
      - 60.29% main
           __libc_start_main
        0.53% __libc_start_main
+    0.68%     0.00%  a.out    [kernel.kallsyms]  [k] apic_timer_interrupt
+    0.68%     0.00%  a.out    [kernel.kallsyms]  [k] smp_apic_timer_interrupt

This shows that among the 86.22% samples in f1() 58.05% are from f2() and 28.17% from main

folded

With long call stack, it may be easier to read them folded (and this is what is used by Brendan Gregg Flame Graphs ):


perf report --call-graph ,,,,caller -g folded --stdio

# Children      Self  Command  Shared Object      Symbol
# ........  ........  .......  .................  ..............................................
#
   100.00%     0.00%  a.out    libc-2.17.so       [.] __libc_start_main
57.78% __libc_start_main;main;f2;f1
27.90% __libc_start_main;main;f1
10.87% __libc_start_main;main
2.24% __libc_start_main;main;f2
0.53% __libc_start_main;f2
    99.47%    10.87%  a.out    a.out              [.] main
57.78% main;f2;f1
27.90% main;f1
10.87% __libc_start_main;main
2.24% main;f2
    86.22%    85.68%  a.out    a.out              [.] f1
57.78% __libc_start_main;main;f2;f1
27.90% __libc_start_main;main;f1
    60.82%     2.77%  a.out    a.out              [.] f2
57.78% f2;f1
2.24% __libc_start_main;main;f2
0.53% __libc_start_main;f2

Here are the number of sample in each call stack. For example, just looking at the top I can see that 57.78% is on main()->f2->f1(). So if I can optimize something there in the number of calls from f2() to f1(), I know that I can address a large part of the response time, and CPU resource. This even without optimizing f1() itself. Remember that there are two ways to improve the performance: do it faster and do it less.


perf report --call-graph ,,,,callee -g folded --stdio

# Children      Self  Command  Shared Object      Symbol
# ........  ........  .......  .................  ..............................................
#
   100.00%     0.00%  a.out    libc-2.17.so       [.] __libc_start_main
100.00% __libc_start_main
    99.47%    10.87%  a.out    a.out              [.] main
99.47% main;__libc_start_main
    86.22%    85.68%  a.out    a.out              [.] f1
58.05% f1;f2;main;__libc_start_main
28.17% f1;main;__libc_start_main
    60.82%     2.77%  a.out    a.out              [.] f2
60.29% f2;main;__libc_start_main
0.53% f2;__libc_start_main
     0.68%     0.00%  a.out    [kernel.kallsyms]  [k] apic_timer_interrupt
     0.68%     0.00%  a.out    [kernel.kallsyms]  [k] smp_apic_timer_interrupt

When folding in the callee order, the focus is on the function itself. Here I can quickly see that f1() is the hotspot, through f1()<-f2()<-main() and f1()<-main() call chains.

filter

Between caller and callee, when you have a very large call stack, with functions called from many places, and and others calling many functions, it may be difficult to zoom on the point where you want to investigate. I do it in two steps in this case. First, a simple `sudo perf top` to see the functions which is on the top of CPU usage:

   PerfTop:     435 irqs/sec  kernel: 6.0%  exact:  0.0% lost: 0/0 drop: 0/617 [1000Hz cycles],  (all, 2 CPUs)
---------------------------------------------------------------------------------------------------------------------------------------------

    15.41%  libsnappy.so.1.3.0                      [.] snappy::RawUncompress
     4.75%  librocksdb.so                           [.] rocksdb::(anonymous namespace)::BytewiseComparatorImpl::Compare
     4.19%  librocksdb.so                           [.] rocksdb::BlockIter::BinarySeek
     2.32%  librocksdb.so                           [.] rocksdb::MemTable::KeyComparator::operator()
     2.14%  librocksdb.so                           [.] rocksdb::BlockIter::ParseNextKey
     2.12%  librocksdb.so                           [.] rocksdb::InternalKeyComparator::Compare

This is the “Self” value: 15% of the samples system-wide are in the snappy::RawUncompress C++ method from the libsnappy.so.1.3.0


sudo perf record -F99 -g --call-graph fp --delay=5 -v -p $(pgrep -d, yb-tserver) -a sleep 10
sudo perf report --call-graph ,,,,callee --symbol-filter=RawUncompress

Here I record more precisely the processes I’m analyzing and filter the report on “RawUncompress”

Here I have the full call stack from the start of the thread down to this snappy::RawUncompress callee function, knowing that this code path accounts for 21.35% of the processes I recorded. This is an example of quick profiling. It will not solve your issue, but will help you to know where you have to look at to reduce the CPU usage. In performance troubleshooting, when you feel like trying to find the needle in the haystack, you should start by finding the haystack and that’s where you need profiling. The event sampling approach is less intrusive than attaching a debugger. And samples are often sufficient to find the hotspot.

Cet article Linux perf-top basics: understand the % est apparu en premier sur Blog dbi services.

How to migrate Oracle Reports to Oracle BI Publisher ?

$
0
0

According to the Oracle Lifetime Support Policy , Oracle is not planning any functional enhancements for Oracle Reports in its terminal release, or any subsequent releases. Oracle Fusion Middleware 12cR2 (12.2.1.3.0) will be Oracle Reports terminal release.

Oracle Report customers can migrate to Oracle BI Publisher (Oracle official tool for oracle reporting) at their own place while continuing to receive extended support as specified by the Oracle Reports Statement of Direction.

Oracle BI Publisher provides Oracle Reports to BI Publisher Conversion Assistant (official documentation) as a tool to convert reports from the Oracle Reports format to the Oracle BI Publisher format.

Let’s test this conversion tool:

Report Conversion Tools Installation

Before to install OR2BIPConvAssist tool, Oracle BI Publisher (BIP) must be installed. Check the Oracle BI Publisher documentation to install it. If a Middleware Home already exists for a fusion products like Oracle Forms/Reports, BIP must be installed into a new Middleware Home.

Go to oracle download website and download the Report Conversion Tools OR2BIPConvAssist.zip:

Unzip the file:

D:\oracle\software>unzip OR2BIPConvAssist.zip
Archive:  OR2BIPConvAssist.zip
creating: bin/
creating: lib/
inflating: bin/OR2BIPConvAssist.bat
inflating: bin/OR2BIPConvAssist.sh
inflating: lib/aolj.jar
inflating: lib/axis.jar
inflating: lib/biputil.jar
inflating: lib/collections.jar
inflating: lib/commons-discovery.jar
inflating: lib/commons-lang.jar
inflating: lib/commons-logging.jar
inflating: lib/help-share.jar
inflating: lib/i18nAPI_v3.jar
inflating: lib/jaxrpc.jar
inflating: lib/jewt4.jar
inflating: lib/mail.jar
inflating: lib/ohj.jar
inflating: lib/ojdbc6.jar
inflating: lib/ojdl.jar
inflating: lib/or2bipconvassist.jar
inflating: lib/oracle_ice.jar
inflating: lib/orai18n-collation.jar
inflating: lib/orai18n-mapping.jar
inflating: lib/orai18n.jar
inflating: lib/orawsdl.jar
inflating: lib/share.jar
inflating: lib/xdo-core.jar
inflating: lib/xdo-server.jar
inflating: lib/xmlparserv2.jar

Include the ORACLE_HOME path as PATH in the report conversion batch file and add ORACLE_HOME in environment variable:

Open Oracle Reports Builder and save the report in format xml:

 

Execute the report with Oracle Report before the conversion:

I expect to have the same report layout after conversion to BIP.

Start DOS or PowerShell in Administrator Mode and launch the Report Conversion Tool:

Click Next

Choose “RDF XML (.xml)” and click Next

Click Next

Choose Source, Target directory and Debug Mode and click Next

Click Next

Click Next

Choose the option to upload the converted reports to Oracle BI Publisher previously installed

Click Next

Click Convert

Click Next

Check we have no errors into Report Conversion Log File and Report Upload to Catalog file.

Oracle Bi Publisher and database configuration

Next step is to configure BI Publisher and create the report objects (Program Units / Formulas Columns) into the database not converted by the conversion tool because not existing into BI Publisher.

The conversion tool creates:

  • Data Model (.xdm file)

  • Report File (.xdo) + a template file (.rtf)

  • One PL/SQL Package with procedures and functions which contains the code of Oracle Reports Program Units and Formula Columns

This PL/SQL package must be compiled in the target database :

All function and procedures start with the character “CF*” like “Column Formulas”, the term used into Oracle Reports which contains PL/SQL code related to columns data model.

The package specification does not compile due to bad syntax in the declaration of some functions “cf_****”

Modify with the correct syntax and retry the compilation:

The package body does not compile because:

  • One of t he function contains some variables not declared

Let’s declared this variable into the package specification and compile it :

And recompile the package body:

Now let’s do the BI Publisher configuration:

Connect to BI Publisher:

Go to Converted Reports location:

The Oracle Report Parameter has been converted successfully:

The Oracle Report Parameter looks like:

The BI Publisher Report parameter looks like after the conversion:

The DataModels must be modified because the one converted is not correct:

Edit the DataModel:

“Edit Data Set” under menu option:

Modify the SQL statement, the formulas columns coming from oracle reports must be rewritten with CASE sql command:

SELECT ALL ABRECHNUNG_VORGANG.ID, 
 ABRECHNUNG_VORGANG.TYP, 
 . . .
 
CASE 
        WHEN UPPER(VERTRAG.VERTRAG_TYP) = 'AGENTUR' AND rechnung.ist_rechnung_lsv = 'J' AND ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE >= 0
        THEN replace(report.get_text_report('*******', 'AGENTUR_LSV_BETRAG_POSITIV', 'BRIEF_TEXT', ADRESSE.SPRACHE), '%%RG_BETRAG%%', report.money_to_string(ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE))
        WHEN UPPER(VERTRAG.VERTRAG_TYP) = 'AGENTUR' AND rechnung.ist_rechnung_lsv = 'J' AND ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE < 0
        THEN replace(report.get_text_report('*******E', 'AGENTUR_LSV_BETRAG_NEGATIV', 'BRIEF_TEXT', ADRESSE.SPRACHE), '%%RG_BETRAG%%', report.money_to_string(abs(ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE)))
        
        WHEN UPPER(VERTRAG.VERTRAG_TYP) = 'AGENTUR' AND rechnung.ist_rechnung_lsv != 'J' AND ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE >= 0
        THEN replace(report.get_text_report('*******', 'AGENTUR_ESR_BETRAG_POSITIV', 'BRIEF_TEXT', ADRESSE.SPRACHE), '%%RG_BETRAG%%', report.money_to_string(ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE))
        WHEN UPPER(VERTRAG.VERTRAG_TYP) = 'AGENTUR' AND rechnung.ist_rechnung_lsv != 'J' AND ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE < 0
        THEN replace(report.get_text_report('******', 'AGENTUR_ESR_BETRAG_POSITIV', 'BRIEF_TEXT', ADRESSE.SPRACHE), '%%RG_BETRAG%%', report.money_to_string(abs(ABRECHNUNG_VORGANG.TOT_NETTO_PRAEMIE)))
     END) CF_BRIEFE_TEXT,
 	. . .
 FROM . . 
 WHERE . . .

Click OK

Do the same for all Data Models

Now let’s focus on the report layout:

First “bad” suprise, The Report Conversion tool does not convert completely the layout, we have to recreate the layout from the beginning.

Oracle BI Publisher Desktop Installation

In BI Publisher, the report layout must be created with Microsoft Office tools.

The first step is to download and install BI Publisher Desktop which adds a AddIns for Microsoft Office products (Word, Excel,etc.) which will be used to create the report layout.

If we use Microsoft Office 32 bits on a Microsoft 64 bits, we have to install last version of Java 32 bits and mix it with Java 64 bits (needed for BI Publisher Server).

Download and Install Download and Install BIP Desktop 11.1.1.9.0 – 32 bits, the only version compatible with Office 2016, 32 bits.

Run As Administrator:

Click OK

Click Next

Click Next

Click Finish

Check you have a new menu “BI Publisher” into Microsft Word and logon to BI Publisher from Microsoft Word:

Now you can either Create a report either open a report template  (the .rtf file generated by the Conversion Report Tool).

Start to edit the report.

Oracle BI Publisher configuration – report configuration

Go to BI Publisher console administration and upload the .xdo file (Click “Upload Resource” into Task menu):

Execute the report (click on View Report link):

Click Apply

If we compare with the current Oracle Report Layout, there is a big difference in the layout.

 

Conclusion:

The Report Conversion Tool does not convert successfully Oracle Reports rdf file:

  • The Parameter screen is the only part converted with success
  • The Data Model must be rewritten, mainly to integrate oracle reports formula columns
  • The layout must me completely rebuild, all fields are in a bad position, some fileds are missing.
  • The PL/SQL package which contains oracle reports program units must be modified since some errors syntax exists. Program Units in Oracle Reports are used in general to calculate oracle reports formulas columns (column where the results comes from a PL/SQL procedures or packages).
  • The oracle report triggers must be refactored since the report conversion tool does not migrate them. This oracle report triggers are responsible to display or hide some fields. Recreate this trigger into BI Publisher is possible with the BI Publisher option “Event Triggers”.

Cet article How to migrate Oracle Reports to Oracle BI Publisher ? est apparu en premier sur Blog dbi services.

A VPC is a private cloud in a public cloud

$
0
0

By Franck Pachot

.
If you are surprised that the first thing you do in a Public Cloud is creating a Virtual Private Cloud, this post is for you. This is a beginner level post. And if you are at that level, interested by what is the Cloud and what is AWS, I recommend our free AWS Discovery Days – I give it next week in French: https://www.dbi-services.com/fr/trainings/aws-discovery-days/

Today, with the “digitalization” trend, people are looking at the cloud as a way to move their IT from their own data centers to a public cloud provider. And then, the first thing they will try to understand is how their current infrastructure matches in cloud terms. Everything runs in virtual machines (VM) on their self managed hypervisor. And the equivalent in public clouds is there with compute services: Amazon EC2, Google VM instances, Azure Virtual Machines, Oracle Compute Instances. Different names for virtual computers. And, besides defining their number of vCPUs and amount of RAM (Instance Type in AWS, Machine Type in Google, VM Size in Azure, Instance Shape in Oracle) and before attaching it to disks (block storage), the first thing to do is define on which network it is connected. This network is called VPC (Virtual Private Cloud) in AWS and Google, Virtual Network in Azure, VCN (Virtual Cloud Network) in Oracle. On-premises you have VLANs and subnets. And you can have private cloud. So why is this called Virtual Private Cloud in a Public Cloud?

Actually, you will never see Amazon talking about AWS as a Public Cloud provider. It is a cloud provider with public services, but today what you run in a public cloud can run as a private cloud: AWS Outpost, Google Anthos, Azure Stack, Oracle Cloud@Customer…

And, first of all, the most “cloudy” services in AWS do not run in a VPC at all. These days, Amazon is celebrating the 15 years of its first service: S3 (Simple Storage Service). This didn’t match with anything existing on-premises. Not because of the Web API (HTTP, FTP WebDAV where already there, and Oracle even had iFS at that time). But because it is a public service: no need to provision a network, and servers. It runs over the Internet. That’s where AWS comes from: Amazon Web Services. You want to store a file and access it? Before, you needed a server. Now, you have a service. That’s what “serverless” means. Of course there are servers, but you don’t know were they are, how much they cost, how many of them… You interact only with a service. And today, being truely “cloud-native” and “serverless” is about having files in S3, code in Lambda, data in DynamoDB, interfaces in SQS, messages in SNS… None of those services requires a VPC. Their endpoint is a public IP address on the Internet.

Now you understand how we can have a Private Cloud in a Public Cloud. For the things that are not serverless, where you must have a dedicated VM, you need virtual servers and virtual network, as in your private cloud. When you create an EC2 instance, you do the same as when you create a VMware VM on your premises, except that it is hosted in a cloud provider’s datacenter, and can share some of its infrastructure.

The data center is, physically, in an Availability Zone. For High Availability, there are multiple Availability Zones in a Region. And for Disaster Recovery, latency or data locality, or simply because of price, you have multiple regions in the world. A VPC belongs to a region and has subnets in the Availability Zones. They are defined by their network identification, which is the CIDR. For example, A VPC in /16 has 16 bits to identify it: the first two bytes are fixed, and the subnet mask is 255.255.0.0 and all the 65536 addresses in this VPC are your addresses (except a few used by the infrastructure itself). Virtual doesn’t mean that they are isolated. One day you may want to peer two VPCs across different accounts or regions and then you must be sure that the CIDR ranges do not overlap. You deploy subnets to put VMs in specific availability zones, isolate logically your components, and isolate their services. Even if you can manage the access to the network services at instance level with Security Groups, you can allow or deny trafic from/to your subnet with NACLs. And you have access to the route table in order to define what and how you can reach other networks. For example, a public facing subnet will route though an Internet Gateway. And if you want to access the public cloud services, like S3 or DynamoDB, you go though the internet gateway, or a gateway endpoint which can expose the internet service without going though the internet.

So, even if you first encounter with AWS is though a VPC you should keep in mind that many services do not belong to a VPC and many do not belong to a region either. Take S3 for example. There’s no VPC. The endpoint is on Internet. And the service is not even regional. You create the bucket in a region, because you want it near your users, but the service is Global. For data, DynamoDB and IoT Core are serverless. But databases still need a server with CPU and RAM. Even Aurora Serverless is running in a VPC. The database server is managed for you, and may be re-sized, and scaled with read replicas. But there’s no relational database that runs of of a VPC.

Cet article A VPC is a private cloud in a public cloud est apparu en premier sur Blog dbi services.

SQL Server – PolyBase Services when listening on all IP is disabled

$
0
0

Introduction

You will find a lot of blogs explaining how to install the PolyBase feature with a SQL Server database instance. You will also learn how to configure it and how to use it in these blogs. But I could not find any solution for one case I was faced to.

When you have several SQL Server instances on a server you will either allocate a fix port per instance or a dedicated IP per instance, or probably both.

Of course if you define a dedicated IP address to your SQL Server instance you will automatically disable the TCP/IP Listen All property and you will set a fix port to this specific IP address.

Doing so, the best practice is to remove the fix port in the IPAll Section of the IP Address configuration.

These are the best practices that every DBA is applying considering the security policies.

What about Polybase services

When installing the PolyBase feature, you have probably noticed the 2 new services running on your server
– SQL Server PolyBase Data Mouvement
– SQL Server PolyBase Engine

Now that your standard configuration is set, you have to restart your services.
Restarting your SQL Server Service will automatically restart the services that are depending on it
– SQL Server Agent
– SQL Server PolyBase Data Mouvement
– SQL Server PolyBase Engine

When the configuration set above will apply, you will have the surprise that the 2 PolyBase services won’t start anymore.

Annoying, especially if the security policy prevent me to enable listening on all IP Addresses.

Here is the trick

In fact the fix is quite simple, I found it after trying multiple setting possibilities.

It seems that the PolyBase services are only checking the port in the IPAll Section and not the one set on the IP address you enabled.
Therefore if you let the IP address Listen All property to “No” and setting the fix port in the IPAll section to the same port set on the dedicated IP address of your SQL Server instance, the PolyBase services will be able to start again

Conlusion

With this configuration you won’t break you policies and best practices using PolyBase feature.

Cet article SQL Server – PolyBase Services when listening on all IP is disabled est apparu en premier sur Blog dbi services.

Oracle – testing resource manager plans?

$
0
0

By Franck Pachot

.
I never remember that in order to use instance caging you need to set a Resource Manager Plan but don’t need to set CPU_COUNT explicitly (was it the case in previous versions?). Here is how to test it quickly in a lab.


SQL> startup force
ORACLE instance started.

SQL> show spparameter resource_manager_plan

SID      NAME                          TYPE        VALUE
-------- ----------------------------- ----------- ----------------------------
*        resource_manager_plan         string

SQL> show spparameter cpu_count

SID      NAME                          TYPE        VALUE
-------- ----------------------------- ----------- ----------------------------
*        cpu_count                     integer

SQL> show parameter resource_manager_plan

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
resource_manager_plan                string

SQL> show parameter cpu_count

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cpu_count                            integer     16

I have a VM with 16 CPU threads, no “cpu_count” or “resource_manager_plan” are set in SPFILE. I restarted the instance (it is a lab) to be sure that nothing is set on scope=memory.

sqlplus / as sysdba @ tmp.sql /dev/null
for i in {1..16} ; do echo "exec loop null; end loop;" | sqlplus -s "c##franck/c##franck" & done >/dev/null
sleep 10
( cd /tmp && git clone https://github.com/tanelpoder/tpt-oracle.git )
sqlplus / as sysdba @ /tmp/tpt-oracle/snapper ash=event+username 30 1 all < /dev/null
pkill -f oracleCDB1A

I run 32 sessions working in memory (simple PL/SQL loops) and look at the sessions with Tanel Poder’s snapper in order to show whether I am ON CPU or in Resource Manager wait. And then kill my sessions in a very hugly fashion (this is a lab)

Nothing set, all defaults: no instance caging

-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)

-------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME
-------------------------------------------------------------------------------
   16.00   (1600%) | ON CPU                              | C##FRANCK
   16.00   (1600%) | ON CPU                              | SYS

--  End of ASH snap 1, end=2021-03-16 17:38:43, seconds=30, samples_taken=96, AAS=32

On my CPU_COUNT=16 (default but not set) instance, I have 32 sessions ON CPU -> no instance caging

Only CPU_COUNT set, no resource manager plan: no instance caging


SQL> alter system set cpu_count=16  scope=memory;
System altered.

I have set CPU_COUNT explicitely to 16 (just checking because this is where I always have a doubt)

-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)

-------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME
-------------------------------------------------------------------------------
   16.00   (1600%) | ON CPU                              | C##FRANCK
   16.00   (1600%) | ON CPU                              | SYS
     .03      (3%) | ON CPU                              |

--  End of ASH snap 1, end=2021-03-16 18:03:50, seconds=5, samples_taken=37, AAS=32

Setting CPU_COUNT manually doesn’t change anything here.


SQL> startup force
ORACLE instance started.

For the next test I reset to the default to show that CPU_COUNT doesn’t have to be set explicitely in order to enable instance caging.

Resource manager set to DEFAULT_CDB_PLAN with default CPU_COUNT: instance caging

SQL> alter system set resource_manager_plan=DEFAULT_CDB_PLAN scope=memory;

System altered.

I have set the default resource manager plan (I’m in multitenant and running from the CDB)


-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)

-------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME
-------------------------------------------------------------------------------
   13.07   (1307%) | ON CPU                              | SYS
   12.20   (1220%) | resmgr:cpu quantum                  | C##FRANCK
    3.80    (380%) | ON CPU                              | C##FRANCK
    2.93    (293%) | resmgr:cpu quantum                  | SYS

--  End of ASH snap 1, end=2021-03-16 18:21:24, seconds=30, samples_taken=94, AAS=32

Here only 16 sessions on average are ON CPU and the others are scheduled out by Resource Manager. Note that there’s a higher priority for SYS than for my user.

Resource manager set to DEFAULT_MAINTENANCE_PLAN with default CPU_COUNT: instance caging

SQL> alter system set resource_manager_plan=DEFAULT_MAINTENANCE_PLAN scope=memory;

System altered.

I have set the default resource manager plan (I’m in multitenant and running from the CDB)


-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)

-------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME
-------------------------------------------------------------------------------
   13.22   (1322%) | ON CPU                              | SYS
   12.31   (1231%) | resmgr:cpu quantum                  | C##FRANCK
    3.69    (369%) | ON CPU                              | C##FRANCK
    2.78    (278%) | resmgr:cpu quantum                  | SYS
     .07      (7%) | ON CPU                              |
     .04      (4%) | resmgr:cpu quantum                  |

--  End of ASH snap 1, end=2021-03-16 18:29:31, seconds=30, samples_taken=95, AAS=32.1

Here only 16 sessions on average are ON CPU and the others are scheduled out by Resource Manager. Again, there’s a higher priority for SYS than for my user.

Same in a PDB


for i in {1..16} ; do echo "exec loop null; end loop;" | ORACLE_PDB_SID=PDB1 sqlplus -s / as sysdba & done >/dev/null
for i in {1..16} ; do echo "exec loop null; end loop;" | sqlplus -s "c##franck/c##franck"@//localhost/PDB1 & done >/dev/null

I’ve changed my connections to connect to the PDB


-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)


----------------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME             | CON_ID
----------------------------------------------------------------------------------------
   14.95   (1495%) | ON CPU                              | SYS                  |      3
   14.27   (1427%) | resmgr:cpu quantum                  | C##FRANCK            |      3
    1.73    (173%) | ON CPU                              | C##FRANCK            |      3
    1.05    (105%) | resmgr:cpu quantum                  | SYS                  |      3
     .01      (1%) | LGWR all worker groups              |                      |      0
     .01      (1%) | ON CPU                              |                      |      0

--  End of ASH snap 1, end=2021-03-16 19:14:52, seconds=30, samples_taken=93, AAS=32

I check the CON_ID to verify that I run in the PDB and here, with the CDB resource manager plan DEFAULT_MAINTENANCE_PLAN the SYS_GROUP (SYSDBA and SYSTEM) can take 90% of CPU. It is the same with DEFAULT_CDB_PLAN.

Adding a Simple Plan


SQL> alter session set container=PDB1;

Session altered.

SQL> BEGIN
  2  DBMS_RESOURCE_MANAGER.CREATE_SIMPLE_PLAN(SIMPLE_PLAN => 'SIMPLE_PLAN1',
  3     CONSUMER_GROUP1 => 'MYGROUP1', GROUP1_PERCENT => 80,
  4     CONSUMER_GROUP2 => 'MYGROUP2', GROUP2_PERCENT => 20);
  5  END;
  6  /

PL/SQL procedure successfully completed.

SQL> alter system set resource_manager_plan=SIMPLE_PLAN1 scope=memory;

System altered.
This is the simple plan example from the documentation (or here). 

-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)

----------------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME             | CON_ID
----------------------------------------------------------------------------------------
    8.59    (859%) | ON CPU                              | SYS                  |      3
    8.51    (851%) | ON CPU                              | C##FRANCK            |      3
    7.49    (749%) | resmgr:cpu quantum                  | C##FRANCK            |      3
    7.41    (741%) | resmgr:cpu quantum                  | SYS                  |      3
     .04      (4%) | ON CPU                              |                      |      0

--  End of ASH snap 1, end=2021-03-16 19:29:54, seconds=30, samples_taken=92, AAS=32

Now, with this simple plan, everything changed. The level 1 gives 100% to SYS_GROUP but it actually got 50%. Level 2 gives 80% and 20% to groups that are not used there. And level 3 gives 100% to OTHER_GROUPS. But those are the levels documented in pre-multitenant.

Mapping my user to one simple plan group


SQL> BEGIN
  2  DBMS_RESOURCE_MANAGER.create_pending_area;
  3  DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING
  4       (DBMS_RESOURCE_MANAGER.ORACLE_USER, 'C##FRANCK', 'MYGROUP2');
  5  DBMS_RESOURCE_MANAGER.validate_pending_area;
  6  DBMS_RESOURCE_MANAGER.submit_pending_area;
  7  END;
  8  /

PL/SQL procedure successfully completed.

I’ve assigned my C##FRANCK user which gets 20% at level 2


-- Session Snapper v4.31 - by Tanel Poder ( http://blog.tanelpoder.com/snapper ) - Enjoy the Most Advanced Oracle Troubleshooting Script on the Planet! :)

----------------------------------------------------------------------------------------
  ActSes   %Thread | EVENT                               | USERNAME             | CON_ID
----------------------------------------------------------------------------------------
   13.20   (1320%) | ON CPU                              | C##FRANCK            |      3
   12.78   (1278%) | resmgr:cpu quantum                  | SYS                  |      3
    3.22    (322%) | ON CPU                              | SYS                  |      3
    2.80    (280%) | resmgr:cpu quantum                  | C##FRANCK            |      3
     .02      (2%) | resmgr:cpu quantum                  |                      |      0
     .01      (1%) | log file parallel write             |                      |      0
     .01      (1%) | ON CPU                              |                      |      0

--  End of ASH snap 1, end=2021-03-16 19:45:52, seconds=30, samples_taken=96, AAS=32

Now my user got 80% of the CPU resource and SYS only 20%

Surprised? In a CDB the “simple plan” is the the same as described in pre-12c documentation – there’s only one level, and 80/20 shares:

The main message here is: test it because what you get may not be what you think… Test and keep it simple.

Cet article Oracle – testing resource manager plans? est apparu en premier sur Blog dbi services.


Foreign Keys in MySQL, SQL, NoSQL, NewSQL

$
0
0

By Franck Pachot

.
In the NoSQL times, it was common to hear thinks like “SQL is bad”, “joins are bad”, “foreign keys are bad”. Just because people didn’t know how to use them, or they were running on a database system with a poor implementation of it. MySQL was very popular because easy to install, but lacking on many optimization features that you find in other open source or commercial databases. Sometimes, I even wonder if this NoSQL thing was not just a NoMySQL at its roots. When people encountered the limitations of MySQL and thought that it was SQL that was limited.

The following twitter thread, and linked articles, mention how DML on a child table can be blocked by DML on the parent. This is not a problem in some occasions (when this parent-child relationship is a composition where you work on the whole within the same transaction) but can be a problem when the parent is shared my many unrelated transactions.

Sample Data

Surprised because I’ve not seen that even in the earliest versions of Oracle, I had to test it. Especially because MySQL and the InnoDB engines have evolved a lot since version 5. I’ll use Gerald Venzl sample data on countries and cities: https://github.com/gvenzl/sample-data/tree/master/countries-cities-currencies because he provides a SQL script that works on all databases without any changes.

MySQL 8.0.23


{
echo "create database if not exists countries;"
echo "use countries;"
curl -s https://raw.githubusercontent.com/gvenzl/sample-data/master/countries-cities-currencies/install.sql
} | mysql

This creates the tables and data with countries, and cities. There is a foreign key from cities that references countries.

Session 1: update the parent row


use countries;
begin;
select * from countries where country_code='CH';
update countries set population=population+1 where country_code='CH';

I’ve updated some info in countries, leaving the transaction opened.


mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from countries where country_code='CH';
+------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------+
| country_id | country_code | name        | official_name       | population | area_sq_km | latitude | longitude | timezone      | region_id |
+------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------+
| CHE        | CH           | Switzerland | Swiss Confederation |    8293000 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU        |
+------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------+
1 row in set (0.00 sec)

mysql> update countries set population=population+1 where country_code='CH';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from countries where country_code='CH';
+------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------+
| country_id | country_code | name        | official_name       | population | area_sq_km | latitude | longitude | timezone      | region_id |
+------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------+
| CHE        | CH           | Switzerland | Swiss Confederation |    8293001 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU        |
+------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------+
1 row in set (0.00 sec)

With this transaction still on-going, I’ll insert, from another session, a new child row for this parent value: another country in Switzerland

Session 2: insert a child


use countries;
begin;
select * from cities where country_id='CHE';
insert into cities values ('CHE1170', 'Aubonne', null, 2750, 'N', 46.49514, 6.39155, null, 'CHE');

This blocks for a while… innodb-lock-wait-timeout defaults to 50 seconds.

Here is the output:


mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from cities where country_id='CHE';
+---------+------+---------------+------------+------------+----------+-----------+----------+------------+
| city_id | name | official_name | population | is_capital | latitude | longitude | timezone | country_id |
+---------+------+---------------+------------+------------+----------+-----------+----------+------------+
| CHE0001 | Bern | NULL          |     422000 | Y          | 46.94809 |   7.44744 | NULL     | CHE        |
+---------+------+---------------+------------+------------+----------+-----------+----------+------------+
1 row in set (0.00 sec)

mysql> insert into cities values ('CHE1170', 'Aubonne', null, 2750, 'N', 46.49514, 6.39155, null, 'CHE');

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

This insert tries to lock the parent row in share mode, and because it is currently being updated by session 1, the lock cannot be acquired. This is a very simple case of contention that can happen in many cases. Still there in MySQL 8

PostgreSQL


curl -s https://raw.githubusercontent.com/gvenzl/sample-data/master/countries-cities-currencies/install.sql | psql

This creates the tables and data with countries, and cities. There is a foreign key from cities that references countries.

Session 1: update the parent row


begin transaction;
select * from countries where country_code='CH';
update countries set population=population+1 where country_code='CH';

I’ve updated some info in countries, leaving the transaction open.


postgres=# begin transaction;
BEGIN
postgres=# select * from countries where country_code='CH';
 country_id | country_code |    name     |    official_name    | population | area_sq_km | latitude | longitude |   timezone    | region_id
------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------
 CHE        | CH           | Switzerland | Swiss Confederation |    8293000 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU
(1 row)

postgres=# update countries set population=population+1 where country_code='CH';
UPDATE 1
postgres=# select * from countries where country_code='CH';
 country_id | country_code |    name     |    official_name    | population | area_sq_km | latitude | longitude |   timezone    | region_id
------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------
 CHE        | CH           | Switzerland | Swiss Confederation |    8293001 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU
(1 row)

With this transaction still on-going, I’ll insert, from another session, a new child row for this parent value: another country in Switzerland

Session 2: insert a child


begin transaction;
select * from cities where country_id='CHE';
insert into cities values ('CHE1170', 'Aubonne', null, 2750, 'N', 46.49514, 6.39155, null, 'CHE');
commit;

I am able to commit my transaction without any locking problem.

Here is the output:


postgres=# begin;
BEGIN
postgres=# select * from cities where country_id='CHE';
 city_id | name | official_name | population | is_capital | latitude | longitude | timezone | country_id
---------+------+---------------+------------+------------+----------+-----------+----------+------------
 CHE0001 | Bern |               |     422000 | Y          | 46.94809 |   7.44744 |          | CHE
(1 row)

postgres=# insert into cities values ('CHE1170', 'Aubonne', null, 2750, 'N', 46.49514, 6.39155, null, 'CHE');
INSERT 0 1
postgres=# commit;
COMMIT

I was able to commit my transaction

Back to session 1


postgres=# select * from countries where country_code='CH';
 country_id | country_code |    name     |    official_name    | population | area_sq_km | latitude | longitude |   timezone    | region_id
------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------
 CHE        | CH           | Switzerland | Swiss Confederation |    8293001 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU
(1 row)

postgres=# select * from cities where country_id='CHE';
 city_id |  name   | official_name | population | is_capital | latitude | longitude | timezone | country_id
---------+---------+---------------+------------+------------+----------+-----------+----------+------------
 CHE0001 | Bern    |               |     422000 | Y          | 46.94809 |   7.44744 |          | CHE
 CHE1170 | Aubonne |               |       2750 | N          | 46.49514 |   6.39155 |          | CHE
(2 rows)

postgres=# commit;
COMMIT

In the first session, I can see immediately the changes because I’m in the default READ COMMITED isolation level. In SERIALIZABLE, I would have seen the new row only after I completed by transaction that started before this concurrent insert.

Oracle

Of course, Oracle can also run those transactions without any lock. There are no row level lock involved in those transactions except for the row that is modified by the transaction. But as long as the referenced key (the primary key of countries here) is not updated, there are no locks in the other table. Only if the key was updated, the index entry on the foreign key would have been “locked” to avoid inserting a row referencing a parent that will be removed.

I’ll not paste a demo here, as I have many ones available from this post: https://franckpachot.medium.com/oracle-table-lock-modes-83346ccf6a41. Oracle is very similar to PostgreSQL here. If you want to know the difference between Oracle and PostgreSQL about foreign keys, I’ve written about that on the CERN blog: https://db-blog.web.cern.ch/blog/franck-pachot/2018-09-unindexed-foreign-keys-oracle-and-postgresql

NoSQL

In NoSQL databases you don’t lock anything and you accept inconsistencies. Actually, all will depend on your data model. You may store a “country” item with a list of cities in a document store. Key: country id. Value: JSON with country attributes and list of cities with their attribute. Here either you lock in the same way MySQL does: don’t touch to the item while another one is modifying it. Or you can store those as multiple items (the “single table model” for DynamoDB for example) and then people can modify country and cities concurrently. They don’t lock each other but of course you may eventually live in a city which belongs to no country… This is the CAP theorem: scalability vs. consistency. You have this choice in your data model: either you cluster all items together in a single document, or you shard them within the datastore.

YugaByteDB

What about distributed databases? Having a foreign key referencing a parent in another node is a bit more complex because there is a compromise to define: wait on an internet latency, or raise an exception to re-try the operation. YugaByte DB is a NewSQL database which aims at full consistency, SQL and ACID, with the maximum scalability and availability possible.

Note that this is a new database, which has still a lot of work in progress in this area, all documented: https://docs.yugabyte.com/latest/architecture/transactions/explicit-locking, and implemented while the users are asking for it. So, if you read this several weeks after the publishing date… re-run the example and you may have good surprises.

I have a 3 nodes YugaByteDB cluster over 3 regions (I have a few Oracle Free Tier tenants, with free VMs always up).

{
curl -s https://raw.githubusercontent.com/gvenzl/sample-data/master/countries-cities-currencies/install.sql
} | /home/opc/yugabyte-2.5.2.0/bin/ysqlsh -h localhost -U yugabyte -d yugabyte -e

This has created and populated the table, auto-sharded into my 3 nodes:


The shards are called “tablets” here and you can see that those PostgreSQL tables (there are multiple APIs in this YugaByte database) have leaders (my replication factor is 1 here) in every node.



[opc@yb-fra-1 ~]$ /home/opc/yugabyte-2.5.2.0/bin/ysqlsh
ysqlsh (11.2-YB-2.5.2.0-b0)
Type "help" for help.

yugabyte=# begin transaction;
BEGIN

yugabyte=# select * from countries where country_code='CH';

 country_id | country_code |    name     |    official_name    | population | area_sq_km | latitude | longitude |   timezone    | region_id
------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------
 CHE        | CH           | Switzerland | Swiss Confederation |    8293000 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU
(1 row)

yugabyte=# update countries set population=population+1 where country_code='CH';
UPDATE 1

yugabyte=#

I have run the same as before: on one session updating the parent table.


[opc@yb-fra-3 ~]$ /home/opc/yugabyte-2.5.2.0/bin/ysqlsh
ysqlsh (11.2-YB-2.5.2.0-b0)
Type "help" for help.

yugabyte=# begin transaction;
BEGIN

yugabyte=# select * from cities where country_id='CHE';

 city_id | name | official_name | population | is_capital | latitude | longitude | timezone | country_id
---------+------+---------------+------------+------------+----------+-----------+----------+------------
 CHE0001 | Bern |               |     422000 | Y          | 46.94809 |   7.44744 |          | CHE
(1 row)

yugabyte=# insert into cities values ('CHE1170', 'Aubonne', null, 2750, 'N', 46.49514, 6.39155, null, 'CHE');

ERROR:  Operation failed. Try again.: d993bd6d-2f76-40ea-9475-84d09e5e0438 Conflicts with higher priority transaction: efd1aaaf-12f9-4819-9e0c-41ee9c6cfb7b

yugabyte=# commit;
ROLLBACK

When inserting a new child, I got a failure because optimistic locking is used there. We don’t wait but serialization conflicts have to cancel one transaction.

Note that it could be one or the other session that is canceled. Running the same example again I was able to insert and commit a child:


yugabyte=# insert into cities values ('CHE1170', 'Aubonne', null, 2750, 'N', 46.49514, 6.39155, null, 'CHE');
INSERT 0 1

yugabyte=# commit;
COMMIT

This is successful but now the session 1 transaction is in conflict.


select * from countries where country_code='CH';

 country_id | country_code |    name     |    official_name    | population | area_sq_km | latitude | longitude |   timezone    | region_id
------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------
 CHE        | CH           | Switzerland | Swiss Confederation |    8293001 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU
(1 row)

yugabyte=# commit;

ERROR:  Operation expired: Transaction expired or aborted by a conflict: 40001

yugabyte=# select * from countries where country_code='CH';

 country_id | country_code |    name     |    official_name    | population | area_sq_km | latitude | longitude |   timezone    | region_id
------------+--------------+-------------+---------------------+------------+------------+----------+-----------+---------------+-----------
 CHE        | CH           | Switzerland | Swiss Confederation |    8293000 |   41277.00 | 47.00016 |   8.01427 | Europe/Zurich | EU
(1 row)

The transaction started in session 1 cannot be completed and must be re-tried.

This is a general idea with distributed databases, SQL or NoSQL, optimistic locking is often preferred for scalability: better be ready to re-retry in the rare case of conflict rather than waiting for lock acquisition on other nodes. But I mentioned that one or the other transaction was cancelled. Which one? at random? Yes, by default each transaction has a random priority assigned. However, when I test or demo the behavior I want predictable results. This is possible by reducing the range of random priority within non-overlapping bounds.

The defaults are between 0 and 1:

yugabyte=# show yb_transaction_priority_upper_bound;
 yb_transaction_priority_upper_bound
-------------------------------------
 1
(1 row)

yugabyte=# show yb_transaction_priority_lower_bound;
 yb_transaction_priority_lower_bound
-------------------------------------
 0
(1 row)

Now, if I set the session 1 in the lower range (yb_transaction_priority_lower_bound=0, yb_transaction_priority_upper_bound=0.4) and the session 2 in the higher range (yb_transaction_priority_lower_bound=0.6, yb_transaction_priority_upper_bound=1), I know that the session 1 will have its transaction aborted on conflict (look at the timestamps, I’ve run blue first, with low priority, then green with higher priority in the bottom session, then red,back to first session where it fails):

But if I set the session 1 in the higher range (yb_transaction_priority_lower_bound=0.6, yb_transaction_priority_upper_bound=1) and the session 2 in the lower range (yb_transaction_priority_lower_bound=0, yb_transaction_priority_upper_bound=0.4) I know that the session 2 will have its transaction failed on conflict (I started with green here in higher priority session 1, then blue and red in the bottom session where it failed):

Again, this is a choice: optimistic locking. Better retry sometimes than locking always.

Data Model

However, even if the database can implement locking with good efficiency, you should think about the data model. Forget about normal forms here. Think of tables as business entities where attributes are tightly coupled together, but coupling between entities is just a possibility. When looking at the domain model, CITY is an entity, and COUNTRY is another. However, it may require some transformation between the domain model and the implementation. There is the COUNTRY as a code, name and which is an aggregation of CITY. And it may be a different entity from COUNTRY as location where people live, with the population number that I’ve updated. Maybe the POPULATION attribute I’ve been updating belongs to another COUNTRY_POPULATION table. Don’t worry about joins: join can scale. And maybe one day this table with have temporality added because population changes and history is interesting to keep. And with this data model, whatever the locking mechanisms, you can update the population and and cities without blocking each-others.

Let me show that in my YugaByte sessions:


YSQL1 20:26:58 create table COUNTRY_POPULATION as select * from COUNTRIES;
SELECT 196
YSQL1 20:27:06 alter table COUNTRY_POPULATION add foreign key (COUNTRY_ID) references COUNTRIES;
ALTER TABLE
YSQL1 20:27:55

I have created another table for the population info, with a foreign key to COUNTRIES. Of course, I should remove the columns from the tables, keeping only the foreign key, but that doesn’t change my example.

The chronology here is: green -> blue -> yellow. Here, the consistency of data is enforced by Foreign Key referential integrity, scalability is ensured by sharding across nodes over the internet, and there’s no lock conflict thanks to my data model.

There’s no reason to remove foreign keys here.

Cet article Foreign Keys in MySQL, SQL, NoSQL, NewSQL est apparu en premier sur Blog dbi services.

Copy or Migrate a SSISDB environment 2.0

$
0
0

Based on the blog “Copy or Migrate a SSISDB environment” of my colleague Christophe, I create a new one named 2.0.

In the precedent script, you need to give the folder name and environment name and he generates all environment variables for these 2 parameters.
In my case, I need to generate all folders and what are in these folders.

The 2.0 script will generate the creation of the folder and the environment name if they not exist in add of all environment variables.
It is a little bit more global! 😛

To create a Folder, I need to generate this type of script:

DECLARE @FolderName nvarchar(128) = NULL; 
SET @FolderName = 'MyFolfer';
IF ((SELECT count(*) from [SSISDB].[catalog].folders where name=@FolderName)=0) 
BEGIN
       EXEC [SSISDB].[catalog].[create_folder] @folder_name=@FolderName; 
END;

To create the environment name, I need to generate this type of script:

 
DECLARE @EnvName nvarchar(128)= NULL;
SET @EnvName = 'DEV_ENV';
IF ((SELECT count(*) from [SSISDB].[catalog].environments where name=@EnvName)=0)
BEGIN
	EXEC [SSISDB].[catalog].[create_environment] @folder_name=@FolderName, @environment_name=@EnvName, @environment_description=N'';

END;

After, we generate like Christophe does,  environment variables:

EXEC [SSISDB].[catalog].[create_environment_variable] @folder_name=@FolderName, @environment_name=@EnvName, @variable_name=N'myVariable',@data_type=N'String',@sensitive=0, @value = N'\\myshare\',@description=N'';

To create all scripts, I will just insert in the migration script both above scripts in my SELECT:

 
SELECT 'DECLARE @FolderName nvarchar(128) = NULL; DECLARE @EnvName nvarchar(128)= NULL;'
SELECT 
'SET @EnvName = ''' + e.name + ''';' + 
'SET @FolderName = ''' + f.name + ''';'+
'IF ((SELECT count(*) from [SSISDB].[catalog].folders where name=@FolderName)=0) 
BEGIN
       EXEC [SSISDB].[catalog].[create_folder] @folder_name=@FolderName; 
END;'
+
'IF ((SELECT count(*) from [SSISDB].[catalog].environments where name=@EnvName)=0)
BEGIN
EXEC [SSISDB].[catalog].[create_environment] @folder_name=@FolderName, @environment_name=@EnvName, @environment_description=N''' + COALESCE(e.description, '') + '''' +'; END;'
+' EXEC [SSISDB].[catalog].[create_environment_variable] 
@folder_name=@FolderName, 
@environment_name=@EnvName, 
@variable_name=N'''+ ev.name + ''', 
@data_type=N'''+ ev.type + ''', 
@sensitive='+ CONVERT(NCHAR,ev.sensitive) +', 
@value = ' + 
CASE ev.sensitive
WHEN 0 THEN 
CASE ev.type 
WHEN 'Date Time' THEN ''''+ CONVERT(NVARCHAR(max),ev.value) + ''''
WHEN 'String' THEN 'N'''+ CONVERT(NVARCHAR(max),ev.value) + ''''
ELSE CONVERT(NVARCHAR(max),ev.value)
END 
WHEN 1 THEN 
'##########'
END + ',
@description=N'''+ ev.description + ''';'
as tsql_EnvVarcreate
FROM SSISDB.catalog.folders f
INNER JOIN SSISDB.catalog.environments e on e.folder_id = f.folder_id
INNER JOIN SSISDB.catalog.environment_variables ev on ev.environment_id = e.environment_id

The first Select is just to declare the 2 variables @FolderName and @EnvName
The second select will create all our environment variables with the folder and environment name.
Here below a screenshot of the result of the script, when I run the script

As you can see, for all environment variables, this script will have the “exist” test and creation of the folder and environment name.
Copy the result of the script (column tsql_EnvCopy) in a copy it in a SSMS Query window connected to the server where you want to deploy your environment
Be careful and verify before running the result script that you have the good name for all folders, environment name and variable name
Last step is just to run the script:

Et voila, I do my first SSIS Catalog environment migration successfully!

Thank you Christophe for showing the way with your first blog 😎

Cet article Copy or Migrate a SSISDB environment 2.0 est apparu en premier sur Blog dbi services.

Swiss Cyber Security Days (SCSD)

$
0
0

The Swiss Cyber Security Days (SCSD) took place on March 10 and 11, 2021

The largest event in Switzerland entirely dedicated to cyber security took place on March 10 and 11, in a 100% digital form.

Despite the health measures related to the coronavirus pandemic, the third edition of the Swiss Cyber Security Days was maintained. Indeed, cyber threats do not know any break.

 

One of the conferences I was able to attend, presented by Marc K. Peter from Dreamlab Technologies, showed us the paradigm shifts that working from home could bring. Indeed, the last twelve months have dramatically changed the way we communicate and work. However, the massive use of teleworking exposes companies to increased risks, and small and medium-sized enterprises (SME) are particularly targeted. A quarter of Swiss SME have already been subject to a cyber attack and more than a third have suffered financial or reputational consequences. It is therefore important to protect oneself. One of the best practices to implement is the awareness of people, the human being remaining the weakest link in the chain in terms of cybersecurity (the global success rate of phishing attacks is estimated to be around 3.4%…).

In 2025, a projection shows that 85% of successful attacks will exploit user configuration and errors.

These are things that the events we are currently facing show us, and which should help us to cope more effectively…

 

Another conference, presented here by Recorded Future Ltd. showed us the importance and the choice of a good password...which sounds quite obvious but is still not always the case. If there are nowadays turnkey software to help break passwords via brute force, it is however possible to drastically mitigate the risks.

This can be done by making users aware of the need to adopt good practices and reflexes when creating/using passwords, by using a password manager, by using multi-factor authentication as soon as possible, but also by adopting good security hygiene for the Information System (IS) such as establishing firewall rules instead of letting all traffic through, by storing passwords in a hashed format and by reducing the exposed surface of the IS.

And hence the importance of security awareness, again and again…

 

One topic which got discussed quite a lot, presented by Deniz Mutlu from Hacknowledge SA, was also if it is useful to use several SIEMs in an enterprise to have a better visibility of the state of the threat? This is an avenue that some companies are exploring today, to try to take advantage of the complementarity of these monitoring tools, in order to respond in an agile way to the state of the threat. A subject that probably deserves deeper reflection, depending on the context of the organization…

 

Presentation by Laurent Deheyer from Eyrapproach of the ISO27701 standard for personal data protection was also a pretty interesting and useful session. This international standard describes the governance and security measures to be implemented for personal data processing, extending two well-known IT security standards: ISO27001, which certifies an IT security management system, and ISO27002, which details the best practices for implementing the necessary security measures.

This standard responds to the problematic of numerous texts on data protection, including the GDPR and the FADP in particular, allowing organizations that adopt it to increase their maturity and demonstrate an active approach to personal data protection. It is probably a good approach to have a look to these standard while facing topics such as GDPR. On our end for instance, we clearly see that having ISO27001 in place makes the adoption of the new FADP version much easier.

 

Another presentation, done by David Gugelmann from Exeon Analytics AG, was the use of AI to detect cyber threats very early in the process. Early enough, in fact, to not expose themselves more than necessary or fear a large-scale attack to bring the organization to its knees or extort.

These new tools are based on the analysis of all the events that occur on an IS and which, thanks to machine learning techniques (supervised or not) will give indications of compromise.

It is shown that the use of such New Generation tools could have shown the attacks that SolarWinds and Cobalt Strike for example had to face long before causing so much damage…

 

How to react to an incident? What response(s) to bring? These questions were discussed on the last session followed, presented by Wandrille Krafft from Lexfo. A good practice to implement is to isolate the evidence without trampling too much on the crime scene.

Then, depending on the level of discretion you wish to adopt (whether the attacker knows he is detected or not), various actions are possible.

Among them, avoiding shutting down the machines completely (because of memory-logged attacks), isolating the infected emails from the network and exporting the evidence for analysis are good practices.

 

As a conclusion as would say it is definitively really interesting to follow which gives a great opportunity to exchange around a wide range of subjects. I’m already looking forward for the next one…hopefully onsite 😉

Cet article Swiss Cyber Security Days (SCSD) est apparu en premier sur Blog dbi services.

PostgreSQL 14: LZ4 compression for TOAST

$
0
0

In PostgreSQL a row or tuple can not span multiple pages (a page is typically 8kB), but of course you can store larger rows and PostgreSQL brakes and compresses these rows into smaller chunks by using a technique called TOAST. Once your table contains a toast-able data type a so-called toast table is created automatically. Up to PostgreSQL 13 you had no choice how the data is compressed, but a recent commit brings the option to use LZ4 as compression method for TOAST. As you can read in the Wikipedia article, LZ4 is all about compression and decompression speed, so let’s have a look.

If you want to make use of this new feature, you need to compile PostgreSQL with support for it:

postgres@debian10pg:/home/postgres/postgresql/ [pgdev] ./configure --help | grep LZ4
  --with-lz4              build with LZ4 support
  LZ4_CFLAGS  C compiler flags for LZ4, overriding pkg-config
  LZ4_LIBS    linker flags for LZ4, overriding pkg-config

For that to work, the operating system needs to have the corresponding libraries installed. For Debian this is:

postgres@debian10pg:/home/postgres/postgresql/ [pgdev] apt search liblz4-dev
Sorting... Done
Full Text Search... Done
liblz4-dev/stable,now 1.8.3-1 amd64 [installed]
  Fast LZ compression algorithm library - development files

Having that ready and PostgreSQL compiled and installed, lets create two tables: One with the default compression and one with the new LZ4 compression:

postgres=# create table t1 ( a text );
CREATE TABLE
postgres=# create table t2 ( a text compression LZ4 );
CREATE TABLE
postgres=# \d+ t1
                                          Table "public.t1"
 Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
 a      | text |           |          |         | extended | pglz        |              | 
Access method: heap

postgres=# \d+ t2
                                          Table "public.t2"
 Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
 a      | text |           |          |         | extended | lz4         |              | 
Access method: heap

Both tables automatically got a toast table attached:

postgres=# select reltoastrelid from pg_class where relname in ('t1','t2');
 reltoastrelid 
---------------
         16387
         16392
(2 rows)

postgres=# select oid,relname from pg_class where oid in (16387,16392 );
  oid  |    relname     
-------+----------------
 16387 | pg_toast_16384
 16392 | pg_toast_16389
(2 rows)

postgres=# \d+ pg_toast.pg_toast_16384
TOAST table "pg_toast.pg_toast_16384"
   Column   |  Type   | Storage 
------------+---------+---------
 chunk_id   | oid     | plain
 chunk_seq  | integer | plain
 chunk_data | bytea   | plain
Owning table: "public.t1"
Indexes:
    "pg_toast_16384_index" PRIMARY KEY, btree (chunk_id, chunk_seq)
Access method: heap

postgres=# \d+ pg_toast.pg_toast_16389
TOAST table "pg_toast.pg_toast_16389"
   Column   |  Type   | Storage 
------------+---------+---------
 chunk_id   | oid     | plain
 chunk_seq  | integer | plain
 chunk_data | bytea   | plain
Owning table: "public.t2"
Indexes:
    "pg_toast_16389_index" PRIMARY KEY, btree (chunk_id, chunk_seq)
Access method: heap

Let’s check if there is a difference if we populate those tables with some dummy data:

postgres=# \timing on
Timing is on.
postgres=# insert into t1(a) select lpad('a',1000000,'a') from generate_series(1,1000);
INSERT 0 1000
Time: 4643.583 ms (00:04.644)
postgres=# insert into t2(a) select lpad('a',1000000,'a') from generate_series(1,1000);
INSERT 0 1000
Time: 314.107 ms
postgres=# truncate table t1,t2;
TRUNCATE TABLE
Time: 4143.579 ms (00:04.144)
postgres=# insert into t1(a) select lpad('a',1000000,'a') from generate_series(1,1000);
INSERT 0 1000
Time: 4759.809 ms (00:04.760)
postgres=# insert into t2(a) select lpad('a',1000000,'a') from generate_series(1,1000);
INSERT 0 1000
Time: 1011.221 ms (00:01.011)
postgres=# truncate table t1,t2;
TRUNCATE TABLE
Time: 41.449 ms
postgres=# insert into t1(a) select lpad('a',1000000,'a') from generate_series(1,1000);
INSERT 0 1000
Time: 4507.416 ms (00:04.507)
postgres=# insert into t2(a) select lpad('a',1000000,'a') from generate_series(1,1000);
INSERT 0 1000
Time: 299.458 ms

This is a huge difference, and I’ve repeated that test several times and got almost the same numbers. That’s really a great improvement regarding speed. But what about the size on disk?

postgres=# select relname,reltoastrelid from pg_class where relname in ('t1','t2');
 relname | reltoastrelid 
---------+---------------
 t1      |         16387
 t2      |         16392
(2 rows)

Time: 3.091 ms
postgres=# select oid,relname from pg_class where oid in (16387,16392);
  oid  |    relname     
-------+----------------
 16387 | pg_toast_16384
 16392 | pg_toast_16389
(2 rows)

Time: 0.836 ms
postgres=# select pg_size_pretty(pg_relation_size('pg_toast.pg_toast_16384'));
 pg_size_pretty 
----------------
 12 MB
(1 row)

Time: 0.506 ms
postgres=# select pg_size_pretty(pg_relation_size('pg_toast.pg_toast_16389'));
 pg_size_pretty 
----------------
 4000 kB
(1 row)

Time: 0.743 ms

Quite impressive. In addition to the speed, we also get a great reduction on disk. Because compression is better with LZ4, we see fewer rows in the toast table for t2:

postgres=# select count(*) from pg_toast.pg_toast_16384;
 count 
-------
  6000
(1 row)

Time: 5.807 ms
postgres=# select count(*) from pg_toast.pg_toast_16389;
 count 
-------
  2000
(1 row)

Of course the string I used here is not very representative, but this new feature really looks promising. There is also a new parameter if you want to change to this behavior globally;

postgres=# show default_toast_compression ;
 default_toast_compression 
---------------------------
 pglz
(1 row)
postgres=# alter system set default_toast_compression = 'lz4';
ALTER SYSTEM
postgres=# select pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)

Cet article PostgreSQL 14: LZ4 compression for TOAST est apparu en premier sur Blog dbi services.

Adding nmon to Oracle OSWatcher

$
0
0

nmon (nmon is short for Nigel’s performance Monitor for Linux on POWER, x86, x86_64, Mainframe & now ARM (Raspberry Pi)) is a nice tool to monitor a Linux system. Originally it came from the AIX-platform, where it is very popular. I asked myself if I can add nmon to the OSWatcher framework to automatically gather nmon data. It is actually possible. Here’s what I did:

First I installed nmon:

In my case (Oracle Enterprise Linux 7.9. downloaded as part of an Oracle 19c DB vagrant) I had the EPEL repository:


[root@oracle-19c6-vagrant ~]# yum search nmon
============================================================ N/S matched: nmon ============================================================
conmon.x86_64 : OCI container runtime monitor
nmon.x86_64 : Nigel's performance Monitor for Linux
xfce4-genmon-plugin.x86_64 : Generic monitor plugin for the Xfce panel

  Name and summary matches only, use "search all" for everything.
[root@oracle-19c6-vagrant ~]# yum install nmon.x86_64
Resolving Dependencies
--> Running transaction check
---> Package nmon.x86_64 0:16g-3.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

===========================================================================================================================================
 Package                    Arch                         Version                            Repository                                Size
===========================================================================================================================================
Installing:
 nmon                       x86_64                       16g-3.el7                          ol7_developer_EPEL                        69 k

Transaction Summary
===========================================================================================================================================
Install  1 Package

Total download size: 69 k
Installed size: 156 k
Is this ok [y/d/N]: y
Downloading packages:
nmon-16g-3.el7.x86_64.rpm                                                                                           |  69 kB  00:00:00     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : nmon-16g-3.el7.x86_64                                                                                                   1/1 
  Verifying  : nmon-16g-3.el7.x86_64                                                                                                   1/1 

Installed:
  nmon.x86_64 0:16g-3.el7                                                                                                                  

Complete!
[root@oracle-19c6-vagrant ~]# which nmon
/bin/nmon
[root@oracle-19c6-vagrant ~]#

REMARK 1: You may download it from here as well.
REMARK 2: I do recommend to download nmonchart as well, so that nice html-files can be generated.

The difficulty with nmon is, that it does not produce output like vmstat by showing an additional line each time. You run it interactively like e.g. top. Alternatively you can produce nmon-files, which can then be used as a source for nmonchart. Here’s an example of an nmon-command to produce a nmon-file:


oracle@oracle-19c6-vagrant:/home/oracle/ [rdbms19] nmon -t -f -s 2 -c 10
oracle@oracle-19c6-vagrant:/home/oracle/ [rdbms19] ps -ef | grep nmon
oracle    5929     1  0 13:57 pts/1    00:00:00 nmon -t -f -s 2 -c 10
oracle@oracle-19c6-vagrant:/home/oracle/ [rdbms19] ls -l *.nmon
-rw-r--r--. 1 oracle oinstall 37793 Mar 23 13:57 oracle-19c6-vagrant_210323_1357.nmon

The command-prompt returns immediately and nmon continues in the background. In the example above I create a snapshot every 2 secs and do 10 snapshots. I.e. nmon would run in the background for 20 seconds here.

The produced file-name follows a naming convention: <host-name>_<YYMMDD>_<HH24MI>.nmon

Fortunately nmon also provides the option “-F” to use a file-name I choose:


oracle@oracle-19c6-vagrant:/home/oracle/ [rdbms19] nmon -t -f -s 2 -c 10 -F ./my_own_nmon_file.dat
oracle@oracle-19c6-vagrant:/home/oracle/ [rdbms19] ls -l my_own_nmon_file.dat 
-rw-r--r--. 1 oracle oinstall 34752 Mar 23 13:58 my_own_nmon_file.dat

To add custom data collections to OSWatcher you have to provide a shell-script and add an entry to the file extras.txt in the OSWatcher home directory (in my case /home/oracle/tools/OSWatcher/oswbb).

An example for an additional data collector has been provided in My Oracle Support Note
How to extend OSW to monitor PeopleSoft domains (Doc ID 1531211.1)

First of all I created my shell-script nmonoswbb.sh :


#!/bin/bash

# get the time between snapshots in secs fron the running OSWatcher script.
TIME_BETWEEN_SNAPSHOTS=$(ps -ef | grep OSWatcher.sh | grep -v grep | tr -s " " | cut -d " " -f10)

# in case OSWatcher.sh was started without parameter I set the TIME_BETWEEN_SNAPSHOTS to the default of 30 secs.
if [ -z "$TIME_BETWEEN_SNAPSHOTS" ]
then
   TIME_BETWEEN_SNAPSHOTS=30
fi

# snapshots we will do until the end of the hour
# this is calculated as follows: seconds_left_this_hour / TIME_BETWEEN_SNAPSHOTS
SNAPSHOTS=$(((3600-(($(date +"%M" | sed 's/^0*//')*60)+$(date +"%S" | sed 's/^0*//')))/${TIME_BETWEEN_SNAPSHOTS}))

OSWBB_ARCHIVE_FILE=$1

if [ ! -f "$OSWBB_ARCHIVE_FILE" ]
then
   echo "zzz ***"`date '+%a %b %e %T %Z %Y'` >> $1
fi

LINES_IN_ARCHIVE=$(cat $OSWBB_ARCHIVE_FILE | wc -l)

if [ $LINES_IN_ARCHIVE -lt 3 ]
then
   # start a new nmon file
   rm -f $OSWBB_ARCHIVE_FILE
   /bin/nmon -t -f -s $TIME_BETWEEN_SNAPSHOTS -c $SNAPSHOTS -F $OSWBB_ARCHIVE_FILE
fi

What I’m doing here is to calculate how many snapshots I have to take to gather nmon-data for the remaining of the current hour. And finally I do start nmon if the archive file (provided from OSWatcher through the parameter $1) has less than 3 lines. At the top of the hour OSWatcher produces an archive-file which initially contains 1 line:


Linux OSWbb v8.4.0

If the file is new (has 1 line) or is not available, I’m starting nmon for the next hour. I.e. in the script above I’m doing an nmon-snapshot every e.g. 60 seconds (TIME_BETWEEN_SNAPSHOTS). I.e. the number of snapshots to do is then calculated as

(3600 – seconds_happened_this_hour) / TIME_BETWEEN_SNAPSHOTS
E.g. if the script was initially started at 14:01:20 then we do have

(3600 – 80)/60 = 58

I.e. nmon will do 58 snapshots (1 every minute) in the remaining 58.5 minutes until 15:00.

Finally we just need the file extras.txt which looks as follows:


# File format is as follows...
# shell_script name directory_name
# where shell_script = name of shell to execute
# name = name of this program
# directory_name is directory name under the archive
nmonoswbb.sh nmon cus_nmon

That means OSWatcher will start nmonoswbb.sh every cycle and put the archives in a folder cus_nmon.
I.e. let’s start OSWatcher to keep the data for 5 hours and snapshot every 20 seconds as follows:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] ./startOSWbb.sh 20 5 gzip
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] Info...Zip option IS specified. 
Info...OSW will use gzip to compress files.
Setting the archive log directory to/home/oracle/tools/OSWatcher/oswbb/archive

Testing for discovery of OS Utilities...
VMSTAT found on your system.
IOSTAT found on your system.
MPSTAT found on your system.
IP found on your system.
TOP found on your system.
Warning... /proc/slabinfo not found on your system. Check to see if this user has permission to access this file.
PIDSTAT found on your system.
NFSIOSTAT found on your system.
Warning... TRACEROUTE not found on your system. No TRACEROUTE data will be collected.

Discovery of CPU CORE COUNT
CPU CORE COUNT will be used by oswbba to automatically look for cpu problems

CPU CORE COUNT = 4
VCPUS/THREADS = 4

Discovery completed.

Starting OSWatcher v8.4.0  on Tue Mar 23 16:47:56 +01 2021
With SnapshotInterval = 20
With ArchiveInterval = 5

OSWatcher - Written by Carl Davis, Center of Expertise,
Oracle Corporation
For questions on install/usage please go to MOS (Note:301137.1)

Data is stored in directory: /home/oracle/tools/OSWatcher/oswbb/archive

Starting Data Collection...

oswbb heartbeat:Tue Mar 23 16:48:02 +01 2021

nmon started to write to the archive-file then:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] ls -l archive/cus_nmon
-rw-r--r--. 1 oracle oinstall 31778 Mar 23 16:48 oracle-19c6-vagrant_nmon_21.03.23.1600.dat
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)]

Here the running nmon-process:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] ps -ef | grep nmon | grep -v grep
oracle   13905     1  0 16:48 pts/0    00:00:00 /bin/nmon -t -f -s 20 -c 35 -F /home/oracle/tools/OSWatcher/oswbb/archive/cus_nmon/oracle-19c6-vagrant_nmon_21.03.23.1600.dat
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] 

After the full hour OSWatcher will zip the file from last hour and a new file is produced. Here the files from an earlier run:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] ls -ltr archive/cus_nmon_old/
-rw-r--r--. 1 oracle oinstall 10920 Mar 23 12:58 oracle-19c6-vagrant_nmon_21.03.23.1200.dat.gz
-rw-r--r--. 1 oracle oinstall 16597 Mar 23 13:58 oracle-19c6-vagrant_nmon_21.03.23.1300.dat.gz
-rw-r--r--. 1 oracle oinstall 53453 Mar 23 14:32 oracle-19c6-vagrant_nmon_21.03.23.1400.dat
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] 

The only thing remaining is to stop nmon when stopping OSWatcher. I copied stopOSWbb.sh to stopOSWbb_nmon.sh and adjusted the file:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] cp -p stopOSWbb.sh stopOSWbb_nmon.sh 
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] vi stopOSWbb_nmon.sh 
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] cat stopOSWbb_nmon.sh 
#!/bin/sh
######################################################################
# stopOSW.sh
# This is the script which terminates all processes associated with
# the OSWatcher program.
######################################################################
# Kill the OSWatcher processes
######################################################################
PLATFORM=`/bin/uname`

case $PLATFORM in
  AIX)
    kill -15 `ps -ef | grep OSWatch | grep -v grep | awk '{print $2}'` 
    ;;
  *)
    kill -15 `ps -e | grep OSWatch | awk '{print $1}'`
    kill -12 `ps -e | grep nmon | awk '{print $1}'`
    ;;
esac
######################################################################
# Clean up heartbeat file from /tmp
######################################################################
rm /tmp/osw.hb
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] 

I.e. I added the line with “kill -12”, because the nmon-developer mentioned on his website that kill -USR2 is the correct way to stop nmon. Then we can also stop OSWatcher including nmon:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] ./stopOSWbb_nmon.sh 
User defined signal 2
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] ps -e | grep nmon
oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)]

REMARK: You only have to consider that after a restart of OSWatcher nmon won’t be active until the next top of the hour.

After gathering data for a while you may produce nice html-files using nmonchart:


oracle@oracle-19c6-vagrant:/home/oracle/tools/OSWatcher/oswbb/ [orclcdb1 (CDB$ROOT)] nmonchart archive/cus_nmon_old/oracle-19c6-vagrant_nmon_21.03.23.1400.dat /tmp/oracle-19c6-vagrant_nmon_21.03.23.1400.html

Then enjoy the useful info the html-file provides.

Cet article Adding nmon to Oracle OSWatcher est apparu en premier sur Blog dbi services.

ORA-32635: not a single cell reference predicate

$
0
0

By Franck Pachot

.
I spent 30 minutes to try to understand this error with a query that I’ve run in many environments for years. So if you google it you may arrive here and the solution may be checking whether you have NLS_COMP=LINGUISTIC


SQL> alter session set nls_comp=linguistic;

Session altered.

SQL> alter session set nls_language=french;

Session modifiee.

SQL> select * from (
  2  select 42 snap_id,'TM' type,'DB time' name,0 microseconds from dual
  3  ) model return updated rows
  4    partition by (snap_id)
  5    dimension by (type,name)
  6    measures (microseconds) ( microseconds['','']=microseconds['TM','DB time'])
  7  /
  measures (microseconds) ( microseconds['','']=microseconds['TM','DB time'])
                                                             *
ERREUR a la ligne 6 :
ORA-32635: ce n'est pas un predicat de reference de cellule unique

SQL> host oerr ora 32635

32635, 00000, "not a single cell reference predicate"
// *Cause: A predicate that is not a single cell reference predicate was specified
//         where a single cell reference predicate was expected.
//         A single cell reference predicate is either a constant expression
//         or a predicate of the form 
// *Action: Make sure that the predicate is a proper single cell
//          reference. In some cases, you might have to put explicit
//          type conversion operators (or casts) on the constant expression.

This error message was not very clear to me. Of course the NLS parameters were set at instance level, so not immediately visible.
I had to reduce my long query to one similar to the one above (because initially I suspected duplicate values in the dimensions but I was wrong) and with one line. Given the misleading clue “you might have to put explicit type conversion operators (or casts) on the constant expression” (I tried to cast to CHAR first) I finally checked at NLS parameters.

So now you have the solution:


SQL> alter session set nls_comp=BINARY;

Session modifiee.

SQL>
SQL> select * from (
  2  select 42 snap_id,'TM' type,'DB time' name,0 microseconds from dual
  3  ) model return updated rows
  4    partition by (snap_id)
  5    dimension by (type,name)
  6    measures (microseconds) ( microseconds['','']=microseconds['TM','DB time'])
  7  /

   SNAP_ID TY NAME    MICROSECONDS
---------- -- ------- ------------
        42                       0

The linguistic comparison may not give a unique result depending on the character set, and that may be the cause for this message. With BINARY comparison (the default) there’s no problem.

By the way, I was not surprised to find nothing in Google or MOS. I know that I may be the only one using the MODEL clause 😉 and database with such NLS settings are rare (hopefully).

Cet article ORA-32635: not a single cell reference predicate est apparu en premier sur Blog dbi services.

Oracle Data Integrator Cloud

$
0
0

Data and applications integration is always a technical challenge for most of the companies. All data integration tool ensures that information is timely, accurate, and consistent across complex systems.

Oracle Data Integrator (ODI) is different than traditional data integration tools in the sense it integrates data in a different orders.

ODI is known as en E-LT because it Extracts data from the source system and Loads and Transforms data into the target system while traditional tools transform the data in an intermediate layer (Staging) between the Source (where the data is Extract) and the Target (where the data is Loaded).

Oracle Data Integrator Cloud removes the complexity in creating real-time operation reporting schema enabling a novice user to perform the creation, initial load, and real time synchronization in just a few clicks. Usually, these types of data integration required assistance from ELT developers and Database administrators.

The goal of this blog is to show you how install ODI in the Cloud.

Sign up for a free credit promotion or purchase an Oracle Cloud subscription and your sign-in credentials.

There are two types of ODI components:

  • Oracle Data Integrator Studio used by Administrators, Developers and Operators for administering the infrastructure (security and topology), reverse-engineering the metadata, developing projects, scheduling, operating and monitoring executions.
  • Oracle Data Integrator Console used (in read only) to access the repositories, peform topology configuration and production operations.

The first step is to create an Oracle Database Cloud Service Instance (Database As A Service)

In the top right menu, go to service user console:

Click on Service User Console

Click On Oracle Database Cloud Service

Click on Go to Console

Click on Create Instance

Choose Instance Name and Database Version and Click Next

Choose Database Name, Administration Password (leave all others default value) and Click Next

Create an SSH key, public and private key

ssh-keygen -t rsa -N "" -b "2048" -C "key comment" -f /drives/c/Users/lfe/Documents/dbi/technique/blog/ODI_cloud/id_rsa

WARNING:

You should not store anything in MobaXterm HOME directoy (/home/mobaxterm): with your current settings, this folder is not "persistent", so it will be cleared at each MobaXterm restart.

If you want to set a "persistent" HOME directory which will not be cleared at each MobaXterm startup, go to MobaXterm settings window and choose a folder in which to store MobaXterm home files.

Generating public/private rsa key pair.
Your identification has been saved in /drives/c/Users/lfe/Documents/dbi/technique/blog/ODI_cloud/id_rsa.
Your public key has been saved in /drives/c/Users/lfe/Documents/dbi/technique/blog/ODI_cloud/id_rsa.pub.
The key fingerprint is:
SHA256:S0Z6qE+BSdANABj3RDOdvEq1hxF5ts3jA4MbG8lGGv4 key comment
The key's randomart image is:
+---[RSA 2048]----+
|+o++*=.+ |
|.. +ooX o |
| .o* X.+ |
| .+o@+= + |
| .o++*S+ . |
| ..E= .o |
| . . . . |
| o |
| . |
+----[SHA256]-----+

Two keys are created :

  • public key : id_rsa.pub
  • private key : id_rsa

Click on “SSHPublic Key Files” and Upload the Public key files just created.

Click Next

Click on Create

The Instance Creation fails:

Let’s go back to the previous screen to replace Oracle Database 19c by Oracle Database 12c Release 2

Now the instance can be created:

The instance is being created…

Wait some minutes till the instance is created (Status Ready)

Let’s try to connect with a SSH client (MobaXterm in our case) to the database machine

Get the Public IP address and let’s add the private key created previously into Mobaxterm

Click OK

Let’s connect to the database

The second step is to provisioning an Oracle Java Cloud Instance

The goal is to configure the Oracle Java Cloud Instance on which we want to install and run Oracle Data Integrator Cloud.

Click On Java Services

Click on Create Instance

Enter the service details:

  • Service Level — From the dropdown, select Oracle Java Cloud Service for Fusion Middleware – Oracle Data Integrator
  • Software Release — Select Oracle Weblogic Server 12c (12.2.1.2)

Click Next

 

Choose the Compute Shape (VM.Standard – 2.0 OCPU, 30 GB of Ram).

Choose the public key created previously

Choose the Instance ODI created previously

Enter SYS credentials

For our case do no configure backup. Of course in a production environment, you have always to set Backup Destination.

Click Next

Click on Create

Wait a moment for the Instance creation…

The Oracle Java Cloud Service Instance (JAAS) is now created and associated with the Oracle Database Cloud Service Instance (DBAAS).

Configure the SSH connection from MobaXterm with the same private key created previously for the database instance machine

The next step is to install Oracle Data Integrator

The good news with the Cloud is that Oracle WebLogic Server is already installed in your Java Cloud Service instance, there is no need to install the Fusion Middleware Infrastructure. It is already there!!!

To interact with the graphical interface into the Java Cloud Server Instance, we have to configure VCN viewer.

Turn Off the lock screen and start the VNC Server

[oracle@oditest-wls-1 upperstack]$ gconftool-2 -s -t bool /apps/gnome-screensaver/lock_enabled false
[oracle@oditest-wls-1 upperstack]$ vncserver -nolisten local -geometry 1680x1050

You will require a password to access your desktops.

Password:
Verify:
Would you like to enter a view-only password (y/n)? y
Password:
Verify:
xauth:  file /u01/app/oracle/tools/home/oracle/.Xauthority does not exist

New 'oditest-wls-1:1 (oracle)' desktop is oditest-wls-1:1

Creating default startup script /u01/app/oracle/tools/home/oracle/.vnc/xstartup
Creating default config /u01/app/oracle/tools/home/oracle/.vnc/config
Starting applications specified in /u01/app/oracle/tools/home/oracle/.vnc/xstartup
Log file is /u01/app/oracle/tools/home/oracle/.vnc/oditest-wls-1:1.log

Open a new local terminal to create an SSH tunnel on the VNC server port on the Administration Server VM

ssh -i C:/Users/lfe/Documents/dbi/technique/blog/ODI_cloud/id_rsa -L 5901:127.0.0.1:5901 opc@140.238.211.108 -N

To have the graphical access for the Oracle Java Cloud Server instance,use the VNC Viewer on the local machine to connect to localhost:5901. You will be prompted for the password you entered in the previous step.

Start VNC Viewer locally



 

From vncviewer, navigate to the /u01/zips/upperstack directory and install ODI

[oracle@oditest-wls-1 upperstack]$ cd /u01/zips/upperstack/
[oracle@oditest-wls-1 upperstack]$ ls -ltr
total 4323160
-r-xr-xr-x. 1 oracle oracle 2027199017 Aug 22  2017 fmw_12.2.1.3.0_odi_generic.jar
-r-xr-xr-x. 1 oracle oracle  186404495 Aug 22  2017 fmw_12.2.1.3.0_odi_generic2.jar
-rwxr-xr-x. 1 oracle oracle 2213306744 Dec 17  2019 ODI.zip
[oracle@oditest-wls-1 upperstack]$ unzip ODI.zip
Archive: ODI.zip
inflating: fmw_12.2.1.3.0_odi_generic.jar
inflating: fmw_12.2.1.3.0_odi_generic2.jar
[oracle@oditest-wls-1 upperstack]$ java -jar fmw_12.2.1.3.0_odi_generic.jar 
Launcher log file is /tmp/OraInstall2021-03-19_09-14-31PM/launcher2021-03-19_09-14-31PM.log.
Extracting the installer . . . . . . . . . Done
Checking if CPU speed is above 300 MHz.   Actual 1995.309 MHz    Passed
Checking monitor: must be configured to display at least 256 colors.   Actual 16777216    Passed
Checking swap space: must be greater than 512 MB.   Actual 8191 MB    Passed
Checking if this platform requires a 64-bit JVM.   Actual 64    Passed (64-bit not required)
Checking temp space: must be greater than 300 MB.   Actual 26195 MB    Passed
Preparing to launch the Oracle Universal Installer from /tmp/OraInstall2021-03-19_09-14-31PM

The installation starts

Click OK

Click Next

Skip Auto Updates and click Next

Choose Enterprise Edition and Click Next

Click Next

Click Install

Now, let’s create the Oracle Data Integrator Repository

[oracle@oditest-wls-1 bin]$ cd /u01/app/oracle/middleware/oracle_common/bin/
[oracle@oditest-wls-1 bin]$ ./rcu

Click Next

Click Next

Choose your PDB database and SYS credentials (DBaaS) created previously and click Next

Click Ok

Check Oracle Data Integrator, leave by default all option checked and click Next

 

Enter password for main and auxiliary schemas and click Next

Click Create Tablespaces and Schemas DEV1_*

Click Close

Update the Java Cloud Instance Domain

est-wls-1 bin]$ cd /u01/app/oracle/tools/home/oracle/Oracle/Middleware/Oracle_Home/oracle_common/common/
[oracle@oditest-wls-1 bin]$ ./config.sh

Click Next

Select Oracle Data Integrator Components and click Next

Leave all default value checked and click Next for all screens

Click Next

Click Finish

The last task is to start ODI Studio and create ODI Agents

[oracle@oditest-wls-1 studio]$ cd /u01/app/oracle/tools/home/oracle/Oracle/Middleware/Oracle_Home/odi/studio/
[oracle@oditest-wls-1 studio]$ ./odi.sh
. . .

 

Create your connection, connect to the Work Repository

Click OK

Go to Topology/Physical Architecture/Agent (right click)/New Agent and create the Physical Agent

 

Go to Topology/Logical Architecture/Agent (right click)/New Agent and create the Logical Agent

 

Now you are ready to create your first mapping from source database to target database.

Conclusion:

To install Oracle Data Integrator into Oracle Cloud Infrastructure, we need to :

  • To access to OCI Generation 1 – OCI Classic (Menu Profile/Service User Console), the OCI generation 2 is reachable from the top left menu
  • Create a database instance (DBaaS) provisioning a machine with Oracle Database already installed used to store the ODI repository.
  • Create a Java instance (JaaS) provisioning a machine with Oracle Fusion Middleware Infrastructure already installed,
  • Install ODI into the JaaS machine.

Cet article Oracle Data Integrator Cloud est apparu en premier sur Blog dbi services.


Should CPU-intensive logic be done in the DB or in application server?

$
0
0

By Franck Pachot

.
Should CPU-intensive logic be done in the DB or in application server? Here was a answer found in Reddit:

Reducing the CPU usage on the database server is a major cost saver with commercial databases, like Oracle Enterprise Edition where you pay license per core. But even in Open Source, on-premises or Cloud, the database servers are usually not the cheapest ones. People often think they can reduce the CPU usage on the DB server by doing more processing on the application server. However, most of the time, this processing operates on data coming from the database, and the result must be saved and shared in the database. Those round-trips take time, but you may not care if the goal is only to reduce the CPU usage on the database server. Still, those roundtrips also use a lot of CPU. Except when you have intense processing to do, it is probably less expensive to run the computation on the database, in the same process that retrieved the data, without any context switch, system call, and with all data already there in RAM or, even better, in CPU caches.

I’ll run a quick test with PostgreSQL:


drop table if exists DEMO;
create unlogged table DEMO as select 'Hello World' txt1, '' txt2 from generate_series(1,100000);

This table is there just to set some rows and my goal is to fill the TXT2 column from TXT1 with some processing.


def cpuintensivework(s,n):
 r=s
 import codecs
 for i in range(n):
  r=codecs.encode(r,'rot13');
 return r;

Here is a Python function to do this processing. Very silly here: it applies Rot-13 multiple times. Calling it 1 million times takes 1.340s on my lab (an Oracle Cloud compute VM with Intel(R) Xeon(R) Platinum 8167M CPU @ 2.00GHz). I’m just simulating some processing, which may implement some business logic that has to run on some data.

I have installed psycopg2 (`pip install psycopg2-binary`) to run the following, which reads rows from my DEMO table and updates TXT2 with the function applied on TXT1:


import psycopg2
conn = psycopg2.connect("dbname='postgres' user='postgres' host='localhost' password='postgres'")
cur = conn.cursor()
cur.execute('select * from DEMO')
for row in cur.fetchall():
    cur.execute("update demo set txt2=%(txt2)s where txt1=%(txt1)s",{"txt1":row[0],"txt2":cpuintensivework(row[0],999999)})
conn.commit();

This is what happens when you deploy all logic in the application server. You have the library there, with your function, get data, process it and put the result back to the database. And you think that you save some database server CPU by doing that in the application server. How CPU intensive is this function? For each value, I apply Rot13 encoding one million times here. Completely silly for sure, but what I want to test is multiple levels of “CPU intensive” workload. So I’ll run that with one million Rot13, then ten thousands, then one thousand… To see when doing that on the application server really saves some CPU usage on the database server.

First I’ve run this with 10 instead of 999999, so very little compute, and check the CPU usage from my database backend process and my python client:


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
23195 postgres  20   0  401884  11020   8820 R 100.0  0.0   5:06.79 postgres: postgres postgres 127.0.0.1(42558) UPDATE
23194 franck    20   0  240968  36824  11284 S   0.0  0.0   0:00.19 python3 /tmp/data2code.py 10

With 10 times encoding on the application tier, the database server process is still 100% busy. This is because, even if there’s a little processing on the client, this is negligible when compared to retrieving the text value sending it to the network stack (even if I’m running both on the same server, those are system calls) waiting for the response…


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
23659 postgres  20   0  401884  11020   8820 R 100.0  0.0   0:45.26 postgres: postgres postgres 127.0.0.1(42730) UPDATE
23658 franck    20   0  240968  36936  11392 S   0.0  0.0   0:00.15 python3 /tmp/data2code.py 100

I’ve run the same with 100 times encoding and still see 100% on the database backend.


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
23788 postgres  20   0  401884  11020   8820 R  99.7  0.0   0:20.00 postgres: postgres postgres 127.0.0.1(42784) UPDATE
23787 franck    20   0  240968  36828  11284 S   0.7  0.0   0:00.23 python3 /tmp/data2code.py 1000

When I have 1000 loops on those Rot13 encoding, I start to see the effect of offloading. Very small: the backend is idle 0.3% of times. For sure, this is not enough to reduce significantly the CPU usage on the database server.


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
23852 postgres  20   0  401884  11020   8820 R  97.0  0.0   0:21.39 postgres: postgres postgres 127.0.0.1(42808) UPDATE
23851 franck    20   0  240968  37064  11520 S   3.0  0.0   0:01.16 python3 /tmp/data2code.py 10000

With 10000 encodings for each value, I start to offload 3% of the CPU time by processing it on the application.


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
24427 postgres  20   0  401884  11020   8820 R  84.7  0.0   0:18.45 postgres: postgres postgres 127.0.0.1(42992) UPDATE
24426 franck    20   0  240968  37028  11484 S  14.3  0.0   0:04.73 python3 /tmp/data2code.py

Now with 50000 loops, this starts to be significant. The database backend is saving 15% of CPU time which can be used to increase the throughput without adding more CPU. This is where processing on the application server starts to make sense if the vCPU there are cheaper than on the database server. So, let’s look at those 50000 encoding loops deeper.

When I run 1000 times my function (with 50000 loops in it) I get:


real    1m19.076s
user    1m2.109s
sys     0m0.111s

79 seconds elapsed time. This means that one call to the function is about 79 millisecond. Or 62 millisecond of CPU if you consider the userspace CPU reported here. This starts to give a rough idea of the “CPU intensive” processing that may significantly reduce the database CPU usage when done on the client side. 60 milliseconds of CPU on a 11 characters string is not usual business logic. Checking a password hash value, comparing account amounts, incrementing a gaming score… all that is much lower. Double-digit millisecond can be CPU intensive encoding, image pattern matching, or compression for example and those are technical and not business logic.

You see the paradox. You prefer to deploy business logic code on the application server, to maintain code easily, in the language of your choice, easy to unit-test… And accept some technical processing withing the database server. But when looking at the performance, it should be the opposite. The business logic applied on data should run within the database backend because it is too fast to justify taking the data to another thread, process or even node. Remember, I’m on the same server here, to count only for CPU usage. But in a real stack, those run on different nodes with network latency.

If CPU time in millisecond doesn’t ring a bell, I’ve run `perf stat`on both application process (python) and database backend (postgres) for this run (50000 Rot13 loops on 1000 items).


 Performance counter stats for 'sh benchsmartdb.sh 1000 50000 data2code':

      61748.646557      task-clock:u (msec)       #    0.786 CPUs utilized
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
             4,483      page-faults:u             #    0.073 K/sec
   144,979,165,329      cycles:u                  #    2.348 GHz
   379,558,933,532      instructions:u            #    2.62  insn per cycle
    86,933,135,095      branches:u                # 1407.855 M/sec
       114,435,352      branch-misses:u           #    0.13% of all branches

this is my python program: 379 billion CPU instructions to process those 1000 items with this CPU intensive function.


 Performance counter stats for process id '27354,28903,28904,28906,28907,28908,28909,28910,28911':

      19589.804909      task-clock (msec)         #    0.233 CPUs utilized
             2,096      context-switches          #    0.107 K/sec
                12      cpu-migrations            #    0.001 K/sec
             1,001      page-faults               #    0.051 K/sec
       150,895,753      cycles                    #    0.008 GHz                      (37.49%)
   101,235,370,509      instructions              #  670.90  insn per cycle           (37.56%)
    21,987,481,044      branches                  # 1122.394 M/sec                    (37.73%)
        12,775,395      branch-misses             #    0.06% of all branches          (37.44%)

this is the whole set of PostgreSQL processes: 100 billion CPU instructions to parse the SQL query, read the data from the database, send it to the application, get the result, update the database. There’s no WAL generation here because I’ve created an UNLOGGED table to account only for CPU and not disk latency.

And actually, most of this CPU usage on the database backend is just an overhead because of the process-on-the-application design. You may have seen that my Python program was called ‘data2code’ because it bring data to the code. But here is the equivalent where the code is deployed to the database:


create extension plpython3u;
select * from pg_language;
create or replace function cpuintensivework(s text,n int) returns text as \$\$
 r=s
 import codecs
 for i in range(n):
  r=codecs.encode(r,'rot13');
 return r;
\$\$ language plpython3u;

I have installed the Python language extension (`sudo yum install -y postgresql12-plpython3`) on my PostgreSQL database. There are plenty of possibilities: PL/pgSQL, Tcl, Perl, and Python are in the base distribution, and there are of course many extensions like Java, Lua, R, bash, JavaScript… This is really the same function. No change to the code to deploy it within the database instead of (or in addition to) the application servers.


import psycopg2
conn = psycopg2.connect("dbname='postgres' user='postgres' host='localhost' password='postgres'")
conn.cursor().execute('update demo set txt2=cpuintensivework(txt1,${loops})')
conn.commit();

My data2code.py is even simpler than code2data.py I’ve run in above tests. Instead of fetching rows, calling the client-side function, and update in a loop, I just call the UPDATE statement which calls the server-side function in the same SQL code. Of course, this can be optimized with prepared statements, and even doing it fully in SQL. But the goal is to keep code simple and calling a server-side function, in your preferred language to code the business logic, is good enough.


$ sh benchsmartdb.sh 1000 50000 code2data

 Performance counter stats for process id '27587,28903,28904,28906,28907,28908,28909,28910,28911':

      71844.295221      task-clock (msec)         #    0.966 CPUs utilized
               225      context-switches          #    0.003 K/sec
                12      cpu-migrations            #    0.000 K/sec
             2,386      page-faults               #    0.033 K/sec
    47,515,695,290      cycles                    #    0.661 GHz                      (37.47%)
   379,912,591,942      instructions              #    8.00  insn per cycle           (37.49%)
    87,239,899,133      branches                  # 1214.291 M/sec                    (37.48%)
        80,074,139      branch-misses             #    0.09% of all branches          (37.49%)

      74.365079178 seconds time elapsed

This is faster, and requires less CPU cycles in total. But of course, if your goal was to reduce the CPU usage on the database server, we have 380 billion instructions there where it was only 100 billion on the backend when doing the intensive processing on the application. So for this CPU intensive function, it might be the right choice. But remember that this function is far from usual business logic (50000 loops, 60 milliseconds of CPU, per call).

But what is interesting is to compare the total. With data2code I had 100 billion instructions on the data server and 379 billion on the application server. With code2data I have 380 billion instructions on the data server and nearly nothing on the application (I didn’t show it here but that’s only one UPDATE call without any processing). All that to do the same thing (same function, on same data, with same code, in same language). There’s a 100 billion overhead here with the data2code design. Because of the communication between multiple processes, though multiple layers. System calls, context switches and not leveraging data locality though CPU cache and RAM.

I focused on the 50000 loops threshold here, about 60 millisecond of CPU usage, hundreds of million CPU instructions per call, where we start to see some offloading to save some DB server CPU usage. I’ll continue with the data2code design with more intensive functions, by incrementing the number of loops.


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
23935 postgres  20   0  401884  11020   8820 R  76.1  0.0   0:20.80 postgres: postgres postgres 127.0.0.1(42826) UPDATE
23934 franck    20   0  240968  36828  11284 S  23.3  0.0   0:10.29 python3 /tmp/data2code.py 100000

With 100000 Rot13 loops in each function call, there’s a clear 1/4th of processing that is done by the application server instead of the database


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
24018 franck    20   0  240968  36872  11328 R  82.0  0.0   0:56.90 python3 /tmp/data2code.py 1000000
24019 postgres  20   0  401884  11020   8820 S  18.3  0.0   0:08.25 postgres: postgres postgres 127.0.0.1(42860) idle in transaction

10x more work done in the function and here the application process is now on Top CPU usage. Here the overhead of roundtrips between multiple nodes is negligeable. And the advantage of offloading is clear. This CPU work can be done by a serverless function, like a lambda, that can scale out quickly when needed or be completely stopped when not used.


  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
24202 franck    20   0  240968  36972  11424 R  99.7  0.0   0:23.04 python3 /tmp/data2code.py 10000000
24203 postgres  20   0  401884  10808   8692 S   0.0  0.0   0:00.19 postgres: postgres postgres 127.0.0.1(42916) idle in transaction

This is 100% processing on the application side. For sure, you don’t want to provision some database server capacity for that and calls to the database are minimal. But keep in mind that this is 10 million loops on a 11 characters text. This is not the usual business logic applied to database data. Very specific use cases only.

I know that bringing code to data may seem more difficult than deploying the code into application servers, which are stateless and use an ORM framework that interacts with the database. But this costs a lot in CPU usage (and even if your money is unlimited, the planet resources are not, and data centers consume them). There are very good articles and talks about Data Gravity. Pulling data to the code is expensive. Pushing the code to the database needs some design considerations, but is generally cheaper. If you have the occasion to listen to Piet de Visser on Microservices and Databases, he has a nice illustration with a famous cartoon character who is able to do it at scale. But remember this hero relies on magic potion. If you don’t, and your business logic is not too CPU intensive, it can be rewarding to look at procedural languages available in your RDBMS. And the good thing is that you have the choice. PostgreSQL can run many languages. Oracle can run PL/SQL or even JavaScript in 21c. And if scale-out is your main reason, distributed databases can also run code on database pods (see Bryn Llewellyn Using Stored Procedures in Distributed SQL Databases).

Cet article Should CPU-intensive logic be done in the DB or in application server? est apparu en premier sur Blog dbi services.

Working with multipass, a VM manager

$
0
0

Introduction

What if you need quickly to launch virtual machine instances running Ubuntu for development and testing purposes.
Multipass from Canonical can be the right tool for this. You can even customize the instances during launch by using Cloud-init in order to simulate a small cloud deployment from your laptop or desktop.
So, in this short blog post, I will talk about this tool and demonstrate how easy it is to use it.

Introduction

Multipass is a lightweight Virtual Machines Manager (VMs) and can be the ideal way for launching VM’s for developers.
It can be installed either on Linux, MacOs or Windows.
Multipass use KVM on linux, Hyperkit on MacOs and Hyper-V on Windows and supports metadata for cloud-init.
It allows you to launch a fresh Ubuntu environment with a single command and it is also possible to simulate a small cloud deployment.

Installation

I’m going now to show you how to install multipass locally on my MacBook but you can also install the tool on Windows or on Linux.
You have 2 options to achieve this. brew (https://brew.sh/), the package manager or the mac installer.
As brew is already installed, let’s do it with brew.
(⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 K8S % brew install --cask multipass
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Updated Formulae
Updated 1 formula.
==> Downloading https://github.com/CanonicalLtd/multipass/releases/download/v1.6.2/multipass-1.6.2+mac-Darwin.pkg
Already downloaded: /Users/sme/Library/Caches/Homebrew/downloads/64f65f71508526f4215f89bec1a12bbc130d408d2bd22f9460fc792fe5aa09f6--multipass-1.6.2+mac-Darwin.pkg
==> Installing Cask multipass
==> Running installer for multipass; your password may be necessary.
Package installers may write to any location; options such as `--appdir` are ignored.
Password:
installer: Package name is multipass
installer: Installing at base path /
installer: The install was successful.
🍺 multipass was successfully installed!

Let’s check the Version
(⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 ~ % multipass version
multipass 1.6.2+mac

Environment setup

To provision and customize the virtual machines to simulate a small cloud deployment, I will use “cloud-init” from Canonical
Let’s create the 3 VMs.
((⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 K8S % curl https://raw.githubusercontent.com/tigera/ccol1/main/control-init.yaml | multipass launch -n control -m 2048M 20.04 --cloud-init -
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6325 100 6325 0 0 11115 0 --:--:-- --:--:-- --:--:-- 11115
Launched: control
(⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 K8S % curl https://raw.githubusercontent.com/tigera/ccol1/main/node1-init.yaml | multipass launch -n node1 20.04 --cloud-init -
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6481 100 6481 0 0 13789 0 --:--:-- --:--:-- --:--:-- 13760
Launched: node1
(⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 K8S % curl https://raw.githubusercontent.com/tigera/ccol1/main/node2-init.yaml | multipass launch -n node2 20.04 --cloud-init -
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6481 100 6481 0 0 12487 0 --:--:-- --:--:-- --:--:-- 12463
Launched: node2

Depending on which platform you are, you may need to start the VMs after they have been launched.
On my desktop, they were all running.
(⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 K8S % multipass list
Name State IPv4 Image
control Running 198.19.0.1 Ubuntu 20.04 LTS
node1 Running 198.19.0.2 Ubuntu 20.04 LTS
node2 Running 198.19.0.3 Ubuntu 20.04 LTS

otherwise start them as following:
(⎈ |docker-desktop:default)sme@Said-MacBook-Pro-3 K8S % multipass start --all
To check and validate if the deployment was successful, log on the first VM and run
ubuntu@control:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 NotReady 5m12s v1.19.3
node2 NotReady 6m33s v1.19.3
control NotReady master 3m12s v1.19.3

As you can notice above, the status is “NotReady” and this is because the CNI (Container Nerwork Interface) plugin is not installed.

Conclusion

That’s all you have to do to install Multipass VM manager and deploy Virtual Machines.
With this tool you can rapidly test your latest developments, so give this tool a chance. In a next blog, I will use this environment and show how to install “Calico”, one of the most famous CNI.

Cet article Working with multipass, a VM manager est apparu en premier sur Blog dbi services.

Oracle JVM included in Oracle Database license ?

$
0
0

Introduction

A customer of mine asked me a licensing question regarding the usage or Oracle JVM in an Oracle database.  Basically this customer have an Oracle Database Standard Edition License and would like to use Oracle JVM in order to load Java classes in his PL/SQL code executed by this database. The customer would like to ensure that his Oracle Database Standard Edition gives him the right to execute Java Classes in his PL/SQL code.

Sources of information

The question even if looking obvious for some people wasn’t so obvious for me since I wasn’t really able to find the information on Oracle documentation nor in a note on My Oracle Support. I started to ask to some of our internal Specialist such as Pascal Brand and Franck Pachot in order to get some piece of answer. I use this opportunity to thanks both of them for their feedbacks ;-).

Both provided me some valuable inputs:

Their understanding was that the Java License is included in Oracle Database Standard Edition according to the following documentation: https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/Licensing-Information.html#GUID-AB354617-6614-487E-A022-7FC9A5A08472

Oracle Database Standard Edition 2 On-Premise SE2 Oracle Database Standard Edition 2 includes features necessary to develop workgroup, department-level, and Web applications.

In addition they provided me some MOS notes were it was more clearly explained such as the note 1557737.1. as well as some link to the Java FAQ where the answer was even more explicit.

“If you are an Oracle Customer with a supported Oracle product which requires Java SE, you continue to have access to Oracle Java updates, as required by your Oracle product, for the use of supported Oracle products, at no additional cost.”

Finally in order to be sure of my answer to the customer I wrote the following Service Request:

Q:
=======
A question regarding Oracle Database Standard Edition and Java.

We would like to use Oracle JVM in order to load Java classes in our PL/SQL code executed by a standard edition database. We would like to ensure that the Oracle Database Standard Edition gives us the right to execute Java Classes in PL/SQL code.

Our understanding is that the Java License is included in Oracle Database Standard Edition according to the following documentation:
https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/Licensing-Information.html#GUID-AB354617-6614-487E-A022-7FC9A5A08472

As far as we do understand we will however have to use the Java version provided by the Oracle Database located in the ORACLE_HOME. According to https://blogs.oracle.com/java-platform-group/oracle-java-se-releases-faq :
“If you are an Oracle Customer with a supported Oracle product which requires Java SE, you continue to have access to Oracle Java updates, as required by your Oracle product, for the use of supported Oracle products, at no additional cost.”

May you please confirm that our understanding is correct, meaning that with Oracle Database Standard Edition, the Oracle Java is included and therefore we can load java classes in PL/SQL code without additional license ?

The answer from the support was crystal clear:

A:
======
Yes, your understanding correct. Here in Oracle there are different Java products.
If you are going to use JAVAVM (Embedded JVM in Database) then DB license is enough. The embedded Database JVM is used to develop and load java source/ classes in database under custom schema.
These java stored procedures are wrapped with pl/sql and invoked by user.
ref: https://docs.oracle.com/en/database/oracle/oracle-database/19/jjdev/Java-application-strategy.html#GUID-492B0C81-24A3-4551-A151-BC0DCE23C802

Thank you.

Conclusion

Following these inputs I’ve been able to inform the customer that even with the Standard Database Edition he is fully allowed to load Java Classes in his PL/SQL Code. If you have doubts regarding usage of Java with Oracle products you will find some interesting information in the following note 1557737.1. I hope this small post will help some of you.

Cet article Oracle JVM included in Oracle Database license ? est apparu en premier sur Blog dbi services.

SQL Server: Create a Shared Storage for your Failover Cluster LAB

$
0
0

I’m currently working on the migration of a Failover Clustered SSAS instance from Windows Server 2012 to Windows Server 2019.

The context is quite complex and in order to choose the right migration scenario I need to play with it on my LAB environment first.
I usually work with AlwaysOn architectures and it’s not often that I need to set up an FCI with its Cluster Shared Volume.

As a reminder, a Cluster Shared Volume is a shared disk containing an NTFS volume that is made accessible for read and write operations by all nodes within a Windows Server Failover Cluster.

In this blog post, will go through the steps I used to create an iSCSI Cluster Shared Volume that will be used to install an SQL Server Failover Cluster Instance (of SSAS) on my Hyper-V LAB.

Windows Server vSAN VM

First, I created a new VM on my Hyper-V Server (my laptop) with Windows Server 2019. You can get a 180 days free trial from the Evaluation Center.
This is a standard VM I named “vSAN”. It will hosts my iSCSI Disks and Targets.
I could have used my Domain Controller VM too as this is just a LAB, but I prefer to have a dedicated VM.

WIN2012VM1 and WIN2012VM2 are the Windows Server 2012 nodes of my Windows Failover Cluster.

iSCSI Server

On vSAN, first we need to install the ISCSI Target Server feature. You can do it with the Server Manager interface or with PowerShell. I’ll do everything with GUI for this post.

For information with PowerShell:

Install-WindowsFeature –Name FS-iSCSITarget-Server –IncludeManagementTools

iSCSI Disk

Always in Server Manager, in “File and Storage Services / iSCSI” click on “Create an iSCSI virtual disk”.

Choose the location where the new disk will be stored. It could be somewhere in C:. I select my larger S: drive.

Enter a name, choose a disk size. I configure my disk to be Dynamically expanding.

 

iSCSI Target

In the following wizard panel, choose “create” then enter an  iSCSI Target name.

 

On the “Access Server” tab, click “Add” and enter the client VM that will access the iSCSI Target.
You could add them by IP address or with DNS Name like I did. Make sure to use the FQDN.

On the next tab, do not enable CHAP.

We are done on the iSCSI Target side. We have a Disk and it’s Target ready. As you can see the status is “Not connected” for now.

iSCSI initiators configuration

Now, on each node of my Windows Failover Cluster, I need to configure the iSCSI initiator to use the Target we just created.

On WIN2012VM1 and WIN2012VM2, in Server Manager click on “iSCSI initiator”.

The iSCSI initiator Properties box should appear.
On Target I enter “vSAN” and click “Quick Connect”.

In the Discovery tab, you can see the Port used by iSCSI is 3260. You might need to add rules in your Firewall to allow TCP traffic on this port.

Click “Auto Configure” on the “Volumes and Devices” tab.

Configure Disk

The iSCSI Target and initiators are now configured.
Looking at the disk on one of the WFC nodes I can see an Offline 10GB disk of bus Type iSCSI.
Let’s initialize it and create a partition. This can be done on one of the nodes only.

Add Disk to Cluster

With the Failover Cluster Manager, I can now add my disk to the Cluster and use it later for my Clustered Instance of SQL Server Engine or SSAS.

That’s it. The new disk is now an Available Storage for my Windows Failover Cluster.

I hope this blog post can help you setting up your Lab and playing with Failover Cluster instances.

Cet article SQL Server: Create a Shared Storage for your Failover Cluster LAB est apparu en premier sur Blog dbi services.

Automating Linux patching with Ansible

$
0
0

Since the beginning of the year, several vulnerabilities have been discovered in the Linux Kernel as well as in others important and widely-used packages. Among them, there was the famous CVE-2021-3156 affecting the sudo package and allowing any unprivileged user to gain root privileges. This one had a base score of 7.8, which is considered as high.
This kind of events demonstrate the importance of having a strong patching strategy to ensure up-to-date softwares and operating systems (even when it’s Linux 😎 ).

When your inventory is composed of few servers, you can easily and quickly patch all of them manually using “dnf/yum update” for RHEL family OS, “apt update” for Debian based OS or with “zypper update” for SUSE servers.
But when you have to update dozens of machines, doing it manually could be time consuming… and boring.
For Enterprise distributions, there are tools that can help you (Red Hat Satellite, SUSE Manager, aso.). But if you want to go with an open source solution, you can use Ansible to create playbooks and roles to patch your servers automatically.

The goal of this blog is not to explain what Ansible is and how does it work. If you have no knowledge on Ansible, it would be better to start by reading the official documentation, which is written in a very simple way and contains a lot of useful exemples.

Playbook

The content of my playbook file is very limited. Its purpose is only to call a role :
---
- name: OS update
  hosts: dev
  gather_facts: yes
  tasks:
    - name: OS update - all packages or security fixes only
      include_role:
        name: os_update 
...

 

Role

The main task of my role is also very short :
---
- include_tasks: redhat.yml
  when: ansible_os_family == "RedHat"
...
Its aim is to call the correct task file depending which operating system the playbook is running for.
The role gives the user the possibility to choose between a full patching or to apply security fixes only (thanks to an extra-var “ev_security_only” you’ll see below). Nothing else in this file, as all the steps are described in the task.

 

Task

The first step is to list all packages that will be modified. Thus, the user can double-check what will be installed before moving on, or cancel the process if desired :
---
- name: Get packages that can be upgraded
  become: yes
  ansible.builtin.dnf:
    list: upgrades
    state: latest
    update_cache: yes 
  register: reg_dnf_output_all
  when: ev_security_only == "no" 

- name: List packages that can be upgraded
  ansible.builtin.debug: 
    msg: "{{ reg_dnf_output_all.results | map(attribute='name') | list }}"
  when: ev_security_only == "no" 


- name: Get packages that can be patched with security fixes
  become: yes
  ansible.builtin.dnf:
    security: yes
    list: updates
    state: latest
    update_cache: yes
  register: reg_dnf_output_secu
  when: ev_security_only == "yes"

- name: List packages that can be patched with security fixes
  ansible.builtin.debug: 
    msg: "{{ reg_dnf_output_secu.results | map(attribute='name') | list }}"
  when: ev_security_only == "yes" 


- name: Request user confirmation
  ansible.builtin.pause:
    prompt: | 

      The packages listed above will be upgraded. Do you want to continue ? 
      -> Press RETURN to continue.
      -> Press Ctrl+c and then "a" to abort.
  when: reg_dnf_output_all is defined or reg_dnf_output_secu is defined

 

Next step is to start the full upgrade or to install the security fixes only :
- name: Upgrade packages
  become: yes
  ansible.builtin.dnf:
    name: '*'
    state: latest
    update_cache: yes
    update_only: no
  register: reg_upgrade_ok
  when: ev_security_only == "no" and reg_dnf_output_all is defined

- name: Patch packages
  become: yes
  ansible.builtin.dnf:
    name: '*'
    security: yes
    state: latest
    update_cache: yes
    update_only: no
  register: reg_upgrade_ok
  when: ev_security_only == "yes" and reg_dnf_output_secu is defined


- name: Print errors if upgrade failed
  ansible.builtin.debug:
    msg: "Packages upgrade failed"
  when: reg_upgrade_ok is not defined

 

If the Kernel has been updated, it’s strongly recommended to reboot the server. To check if a reboot is required, we can use the command “needs-restarting” provided here by the package dnf-utils :
- name: Install dnf-utils
  become: yes
  ansible.builtin.dnf:
    name: 'dnf-utils'
    state: latest
    update_cache: yes

- name: Check if a reboot is required
  become: yes
  command: needs-restarting -r
  register: reg_reboot_required
  ignore_errors: yes
  failed_when: false
  changed_when: reg_reboot_required.rc != 0
  notify:
    - Reboot server 
...

 

Handler

As you can see above, an Handler is called by the “notify” directive.  Using an Handler is very suitable here, as we want to reboot the server once the patching is done, but only when the Kernel has been updated.
---
- name : Reboot server
  ansible.builtin.reboot:
    msg: "Reboot initiated by Ansible after OS update"
    reboot_timeout: 3600
    test_command: uptime
...

 

Execution

Let’s start the playbook to perform a full update (ev_security_only=no) on a single host :
$ ansible-playbook playbooks/os_update.yml --extra-vars "ev_security_only=no"

PLAY [OS update] *****************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************************************************************************
ok: [192.168.22.101]

TASK [OS update - all packages or security fixes only] ***************************************************************************************************************************************************************************************

TASK [os_update : include_tasks] *************************************************************************************************************************************************************************************************************
included: .../roles/os_update/tasks/redhat.yml for 192.168.22.101

TASK [os_update : Get packages that can be upgraded] *****************************************************************************************************************************************************************************************
ok: [192.168.22.101]

TASK [os_update : List packages that can be upgraded] ****************************************************************************************************************************************************************************************
ok: [192.168.22.101] => {
"changed": false,
"msg": [
"tuned",
"hwdata",
"libstdc++",
"libgomp",
"libgcc",
"openssl-libs",
"openssl",
"tuned",
"hwdata",
"python36",
"libstdc++-devel",
"tuned"
]
}

TASK [os_update : Get packages that can be patched with security fixes] *****************************************************************************************************************************
skipping: [192.168.22.101]

TASK [os_update : List packages that can be patched with security fixes] *********************************************************************************************************************************************************************
skipping: [192.168.22.101]

TASK [os_update : Request user confirmation] *************************************************************************************************************************************************************************************************
[os_update : Request user confirmation]

The packages listed above will be upgraded. Do you want to continue ?
-> Press RETURN to continue.
-> Press Ctrl+c and then "a" to abort.
:
ok: [192.168.22.101]

TASK [os_update : Upgrade packages] **********************************************************************************************************************************************************************************************************
changed: [192.168.22.101]

TASK [os_update : Patch packages] ************************************************************************************************************************************************************************************************************
skipping: [192.168.22.101]

TASK [os_update : Print errors if upgrade failed] ********************************************************************************************************************************************************************************************
skipping: [192.168.22.101]

TASK [os_update : Install dnf-utils] *********************************************************************************************************************************************************************************************************
ok: [192.168.22.101]

TASK [os_update : Check if a reboot is required] *********************************************************************************************************************************************************************************************
ok: [192.168.22.101]

PLAY RECAP ***********************************************************************************************************************************************************************************************************************************
192.168.22.101 : ok=8 changed=1 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0

$

Possible improvements

At this stage, this role is rather basic. It could therefore benefit from some improvements, such as :

  • Create tasks to patch Debian based and SUSE servers
  • Add pre-task to send the list of packages by email before starting the patching
  • Logging
  • ….

Perhaps in a next blog…

Cet article Automating Linux patching with Ansible est apparu en premier sur Blog dbi services.

Viewing all 2832 articles
Browse latest View live