Elixir postgrex with poolboy example on Windows fails with 'module DBConnection.Poolboy not available' - windows-10

I am exploring using Elixir for fast Postgres data imports of mixed types (CSV, JSON). Being new to Elixir, I am following the the example given in the youtube video "Fast Import and Export with Elixir and Postgrex - Elixir Hex package showcase" (https://www.youtube.com/watch?v=YQyKRXCtq4s). The basic mix application works until the point that Poolboy is introduced, i.e. Postgrex successfully loads records into the database using a single connection.
When I try to follow the Poolboy configuration, and test it by running
FastIoWithPostgrex.import("./data_with_ids.txt")
in iex or the command line, I get the following error, for which I can not determine the cause (username and password removed):
** (UndefinedFunctionError) function DBConnection.Poolboy.child_spec/1 is
undefined (module DBConnection.Poolboy is not available)
DBConnection.Poolboy.child_spec({Postgrex.Protocol, [types:
Postgrex.DefaultTypes, name: :pg, pool: DBConnection.Poolboy, pool_size: 4,
hostname: "localhost", port: 9000, username: "XXXX", password:
"XXXX", database: "ASDDataAnalytics-DEV"]})
(db_connection) lib/db_connection.ex:383: DBConnection.start_link/2
(fast_io_with_postgrex) lib/fast_io_with_postgrex.ex:8:
FastIoWithPostgrex.import/1
I am running this on Windows 10, connecting to a PostgreSQL 10.x Server through a local SSH tunnel. Here is the lib/fast_io_with_postgrex.ex file:
defmodule FastIoWithPostgrex do
#moduledoc """
Documentation for FastIoWithPostgrex.
"""
def import(filepath) do
{:ok, pid} = Postgrex.start_link(name: :pg,
pool: DBConnection.Poolboy,
pool_size: 4,
hostname: "localhost",
port: 9000,
username: "XXXX", password: "XXXX", database: "ASDDataAnalytics-DEV")
File.stream!(filepath)
|> Stream.map(fn line ->
[id_str, word] = line |> String.trim |> String.split("\t", trim: true, parts: 2)
{id, ""} = Integer.parse(id_str)
[id, word]
end)
|> Stream.chunk_every(10_000, 10_000, [])
|> Task.async_stream(fn word_rows ->
Enum.each(word_rows, fn word_sql_params ->
Postgrex.transaction(:pg, fn conn ->
IO.inspect Postgrex.query!(conn, "INSERT INTO asdda_dataload.words (id, word) VALUES ($1, $2)", word_sql_params)
# IO.inspect Postgrex.query!(pid, "INSERT INTO asdda_dataload.words (id, word) VALUES ($1, $2)", word_sql_params)
end , pool: DBConnection.Poolboy, pool_timeout: :infinity, timeout: :infinity)
end)
end, timeout: :infinity)
|> Stream.run
end # def import(file)
end
Here is the mix.exs file:
defmodule FastIoWithPostgrex.MixProject do
use Mix.Project
def project do
[
app: :fast_io_with_postgrex,
version: "0.1.0",
elixir: "~> 1.7",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger, :poolboy, :connection]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git",
tag: "0.1.0"},
{:postgrex, "~>0.14.1"},
{:poolboy, "~>1.5.1"}
]
end
end
Here is the config/config.exs file:
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
config :fast_io_with_postgrex, :postgrex,
database: "ASDDataAnalytics-DEV",
username: "XXXX",
password: "XXXX",
name: :pg,
pool: DBConnection.Poolboy,
pool_size: 4
# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.
# You can configure your application as:
#
# config :fast_io_with_postgrex, key: :value
#
# and access this configuration in your application as:
#
# Application.get_env(:fast_io_with_postgrex, :key)
#
# You can also configure a 3rd-party app:
#
# config :logger, level: :info
#
# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env()}.exs"
Any assistance with finding the cause of this error would be greatly appreciated!

I didn't want to goo to deep into how this isn't working, but that example is a little old, and that poolboy 1.5.1 you get pulled with deps.get is from 2015.. and the example uses elixir 1.4
Also, if you see Postgrex's mix.exs deps, you will notice your freshly installed lib (1.14) depends on elixir_ecto/db_connection 2.x
The code you are referring uses Postgres 1.13.x, which depends on {:db_connection, "~> 1.1"}. So i would expect incompatibilities.
I would play with the versions of the libs you see in examples code mix.lock file, an the elixir version if I wanted to see that working.
Maybe try lowering the Postgrex version first to something around that time (maybe between 0.12.2 and the locked version of the example).
Also, the version of elixir might have some play here, check this
Greetings!
have fun
EDIT:
You can use DBConnection.ConnectionPool instead of poolboy and get way using the latest postgrexand elixir versions, not sure about the performance difference but you can compare, just do this:
on config/config.exs (check if you need passwords, etc..)
config :fast_io_with_postgrex, :postgrex,
database: "fp",
name: :pg,
pool: DBConnection.ConnectionPool,
pool_size: 4
And in lib/fast_io_with.....ex replace both Postgrex.start_link(... lines with:
{:ok, pid} = Application.get_env(:fast_io_with_postgrex, :postgrex)
|> Postgrex.start_link
That gives me:
mix run -e 'FastIoWithPostgrex.import("./data_with_ids.txt")'
1.76s user 0.69s system 106% cpu 2.294 total
on Postgrex 0.14.1 and Elixir 1.7.3

Thank you, using your advice I got the original example to work by downgrading the dependency versions in the mix.exs file and adding a dependency to an earlier version of db_connection:
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
{:postgrex, "0.13.5"},
{:db_connection, "1.1.3"},
{:poolboy, "~>1.5.1"}
]
end
I will also try your suggestion of changing the code to replace Poolboy with the new pool manager in the later version of db_connection to see if that works as well.
I'm sure there was a lot of thought that went into the architecture change, however I must say there is very little out there in terms of why Poolboy was once so popular, but in the latest version of db_connection it's not even supported as a connection type.

Related

pre-commit hook yapf returns different results than running yapf in the command line

When running over a file using the command line and yapf, my tags are the following:
-i --verbose --style "google"
When using the same above as args for pre-commit, my pre-commit hook always returns "Pass".
This was tested against the same file for the same changes, so I would have expected similar results. If I exclude --style "google", my pre-commit hook will at least change the format of my file, but not to the style that I want it to.
Can someone tell me what I am doing wrong with the args?
Python File that contains an example:
def hello_world():
print("hello world")
if 5 == 5: print("goodbye world")
.pre-commit-config.yaml file:
- repo: https://github.com/pre-commit/pre-commit-hooks.git
sha: v4.0.1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/google/yapf
rev: v0.31.0
hooks:
- id: yapf
name: "yapf"
On commit, my file will change and pre-commit has told me yapf has changed my file to the following:
def hello_world():
print("hello world")
if 5 == 5: print("goodbye world")
If I go back to the same python file and update my .pre-commit-config.yaml file to this:
- repo: https://github.com/pre-commit/pre-commit-hooks.git
sha: v4.0.1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/google/yapf
rev: v0.31.0
hooks:
- id: yapf
name: "yapf"
args: [--style "google" ]
Running a commit will provide Pass instead of making any changes, even the ones from before
Edit 1:
The .pre-commit.config.yaml file was updated to:
- repo: https://github.com/pre-commit/pre-commit-hooks.git
sha: v4.0.1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/google/yapf
rev: v0.31.0
hooks:
- id: yapf
name: "yapf"
args: [--style, google]
Running pre-commit run only shows Passed instead of reformatting. I've also tried putting in pep8, and other arbitrary words as a replacement for google. These all result in Passed. Maybe there is something on my end where the style arg is being ignored and causing all of yapf to fail?
This post was from a while ago, but for anyone else in the future reading it, but I was able to control the yapf style in pre-commit with a .style.yapf in my parent directory, as outlined in the yapf documentation: https://github.com/google/yapf
This was the .style.yapf I used
[style]
based_on_style = google

Azure-ML Deployment does NOT see AzureML Environment (wrong version number)

I've followed the documentation pretty well as outlined here.
I've setup my azure machine learning environment the following way:
from azureml.core import Workspace
# Connect to the workspace
ws = Workspace.from_config()
from azureml.core import Environment
from azureml.core import ContainerRegistry
myenv = Environment(name = "myenv")
myenv.inferencing_stack_version = "latest" # This will install the inference specific apt packages.
# Docker
myenv.docker.enabled = True
myenv.docker.base_image_registry.address = "myazureregistry.azurecr.io"
myenv.docker.base_image_registry.username = "myusername"
myenv.docker.base_image_registry.password = "mypassword"
myenv.docker.base_image = "4fb3..."
myenv.docker.arguments = None
# Environment variable (I need python to look at folders
myenv.environment_variables = {"PYTHONPATH":"/root"}
# python
myenv.python.user_managed_dependencies = True
myenv.python.interpreter_path = "/opt/miniconda/envs/myenv/bin/python"
from azureml.core.conda_dependencies import CondaDependencies
conda_dep = CondaDependencies()
conda_dep.add_pip_package("azureml-defaults")
myenv.python.conda_dependencies=conda_dep
myenv.register(workspace=ws) # works!
I have a score.py file configured for inference (not relevant to the problem I'm having)...
I then setup inference configuration
from azureml.core.model import InferenceConfig
inference_config = InferenceConfig(entry_script="score.py", environment=myenv)
I setup my compute cluster:
from azureml.core.compute import ComputeTarget, AksCompute
from azureml.exceptions import ComputeTargetException
# Choose a name for your cluster
aks_name = "theclustername"
# Check to see if the cluster already exists
try:
aks_target = ComputeTarget(workspace=ws, name=aks_name)
print('Found existing compute target')
except ComputeTargetException:
print('Creating a new compute target...')
prov_config = AksCompute.provisioning_configuration(vm_size="Standard_NC6_Promo")
aks_target = ComputeTarget.create(workspace=ws, name=aks_name, provisioning_configuration=prov_config)
aks_target.wait_for_completion(show_output=True)
from azureml.core.webservice import AksWebservice
# Example
gpu_aks_config = AksWebservice.deploy_configuration(autoscale_enabled=False,
num_replicas=3,
cpu_cores=4,
memory_gb=10)
Everything succeeds; then I try and deploy the model for inference:
from azureml.core.model import Model
model = Model(ws, name="thenameofmymodel")
# Name of the web service that is deployed
aks_service_name = 'tryingtodeply'
# Deploy the model
aks_service = Model.deploy(ws,
aks_service_name,
models=[model],
inference_config=inference_config,
deployment_config=gpu_aks_config,
deployment_target=aks_target,
overwrite=True)
aks_service.wait_for_deployment(show_output=True)
print(aks_service.state)
And it fails saying that it can't find the environment. More specifically, my environment version is version 11, but it keeps trying to find an environment with a version number that is 1 higher (i.e., version 12) than the current environment:
FailedERROR - Service deployment polling reached non-successful terminal state, current service state: Failed
Operation ID: 0f03a025-3407-4dc1-9922-a53cc27267d4
More information can be found here:
Error:
{
"code": "BadRequest",
"statusCode": 400,
"message": "The request is invalid",
"details": [
{
"code": "EnvironmentDetailsFetchFailedUserError",
"message": "Failed to fetch details for Environment with Name: myenv Version: 12."
}
]
}
I have tried to manually edit the environment JSON to match the version that azureml is trying to fetch, but nothing works. Can anyone see anything wrong with this code?
Update
Changing the name of the environment (e.g., my_inference_env) and passing it to InferenceConfig seems to be on the right track. However, the error now changes to the following
Running..........
Failed
ERROR - Service deployment polling reached non-successful terminal state, current service state: Failed
Operation ID: f0dfc13b-6fb6-494b-91a7-de42b9384692
More information can be found here: https://some_long_http_address_that_leads_to_nothing
Error:
{
"code": "DeploymentFailed",
"statusCode": 404,
"message": "Deployment not found"
}
Solution
The answer from Anders below is indeed correct regarding the use of azure ML environments. However, the last error I was getting was because I was setting the container image using the digest value (a sha) and NOT the image name and tag (e.g., imagename:tag). Note the line of code in the first block:
myenv.docker.base_image = "4fb3..."
I reference the digest value, but it should be changed to
myenv.docker.base_image = "imagename:tag"
Once I made that change, the deployment succeeded! :)
One concept that took me a while to get was the bifurcation of registering and using an Azure ML Environment. If you have already registered your env, myenv, and none of the details of the your environment have changed, there is no need re-register it with myenv.register(). You can simply get the already register env using Environment.get() like so:
myenv = Environment.get(ws, name='myenv', version=11)
My recommendation would be to name your environment something new: like "model_scoring_env". Register it once, then pass it to the InferenceConfig.

Can't set environment variables for container in ApplicationLoadBalancedFargateService

(CDK 1.18.0 and Python 3.6)
task_role = iam.Role(
self,
id=f"...",
assumed_by=iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
managed_policies=[...]
)
repo = get_repo(self)
task_def = ecs.FargateTaskDefinition(
self,
"...",
memory_limit_mib=30720,
cpu=4096,
task_role=task_role,
execution_role=self.ecs_execution_role,
)
cont = task_def.add_container(
"...",
image=ecs.ContainerImage.from_ecr_repository(repo),
logging=ecs.LogDrivers.aws_logs(stream_prefix=f"Logging"),
command=["bash", "start.sh"],
environment={"NAME1": 'VALUE1', "NAME2": 'VALUE2'} # what would I have to put here?
)
cont.add_port_mappings(ecs.PortMapping(container_port=8080))
fg = ecsp.ApplicationLoadBalancedFargateService(
self,
"...",
task_definition=task_def,
assign_public_ip=True,
)
I want to pass NAME1=VALUE1 and NAME2=VALUE2 to the container.
I tried various ways to express the environment variables. But none worked. Am I doing something fundamentally wrong here?
Other than this specific issue the service deploys and runs.
The approach you follow seems to work here on the latest version (1.23.0). But I could not find any hint in the release notes why this might have changed. Can you update to the latest version?
task_def.add_container("container", environment={"a": "b", "c": "d"}, image=aws_ecs.ContainerImage.from_registry(name="TestImage"), memory_limit_mib=512)
newtask1C300F30:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
- Environment:
- Name: a
Value: b
- Name: c
Value: d
Essential: true
Image: TestImage
Memory: 512
Name: container

How to tell what "features" are available per crate?

Is there a standard way to determine what features are available for a given crate?
I'm trying to read Postgres timezones, and this says to use the crate postgres = "0.17.0-alpha.1" crate's with-time or with-chrono features.
When I try this in my Cargo.toml:
[dependencies]
postgres = { version = "0.17.0-alpha.1", features = ["with-time"] }
I get this error:
error: failed to select a version for `postgres`.
... required by package `mypackage v0.1.0 (/Users/me/repos/mypackage)`
versions that meet the requirements `^0.17.0-alpha.1` are: 0.17.0, 0.17.0-alpha.2, 0.17.0-alpha.1
the package `mypackage` depends on `postgres`, with features: `with-time` but `postgres` does not have these features.
Furthermore, the crate page for postgres 0.17.0 says nothing about these features, so I don't even know if they're supposed to be supported or not.
It seems like there would be something on docs.rs about it?
Crates uploaded to crates.io (and thus to docs.rs) will show what feature flags exist. crates.io issue #465 suggests placing the feature list on the crate's page as well.
Beyond that, the only guaranteed way to see what features are available is to look at the Cargo.toml for the crate. This generally means that you need to navigate to the project's repository, find the correct file for the version you are interested in, and read it.
You are primarily looking for the [features] section, but also for any dependencies that are marked as optional = true, as optional dependencies count as an implicit feature flag.
Unfortunately, there's no requirement that the crate author puts any documentation about what each feature flag does. Good crates will document their feature flags in one or more locations:
As comments in Cargo.toml
Their README
Their documentation
See also:
How do you enable a Rust "crate feature"?
For the postgres crate, we can start at crates.io, then click "repository" to go to the repository. We then find the right tag (postgres-v0.17.0), then read the Cargo.toml:
[features]
with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"]
with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"]
with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"]
with-geo-types-0_4 = ["tokio-postgres/with-geo-types-0_4"]
with-serde_json-1 = ["tokio-postgres/with-serde_json-1"]
with-uuid-0_8 = ["tokio-postgres/with-uuid-0_8"]
You can use the cargo-feature crate to view and manage your dependencies' features:
> cargo install cargo-feature --locked
> cargo feature postgres
Avaliable features for `postgres`
array-impls = ["tokio-postgres/array-impls"]
with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"]
with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"]
with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"]
with-eui48-1 = ["tokio-postgres/with-eui48-1"]
with-geo-types-0_6 = ["tokio-postgres/with-geo-types-0_6"]
with-geo-types-0_7 = ["tokio-postgres/with-geo-types-0_7"]
with-serde_json-1 = ["tokio-postgres/with-serde_json-1"]
with-smol_str-01 = ["tokio-postgres/with-smol_str-01"]
with-time-0_2 = ["tokio-postgres/with-time-0_2"]
with-time-0_3 = ["tokio-postgres/with-time-0_3"]
with-uuid-0_8 = ["tokio-postgres/with-uuid-0_8"]
with-uuid-1 = ["tokio-postgres/with-uuid-1"]
Note: this only works if the crate is already in Cargo.toml as a dependency.
The list of features are also displayed when using cargo add:
> cargo add postgres
Updating crates.io index
Adding postgres v0.19.4 to dependencies.
Features:
- array-impls
- with-bit-vec-0_6
- with-chrono-0_4
- with-eui48-0_4
- with-eui48-1
- with-geo-types-0_6
- with-geo-types-0_7
- with-serde_json-1
- with-smol_str-01
- with-time-0_2
- with-time-0_3
- with-uuid-0_8
- with-uuid-1
It seems like there would be something on docs.rs about it?
Other people thought so too! This was added to docs.rs in late 2020. You can view the list of features from the crate's documentation by navigating to "Feature flags" on the top bar:
You still won't see the list of features of postgres 0.17.0 though since old documentation isn't regenerated when new functionality is added to the site, but any recently published versions will have it available.
Note: this is only available on docs.rs and not generated when running cargo doc.
The feature-set of a dependency can be retrieved programmatically via the cargo-metadata crate:
use cargo_metadata::MetadataCommand;
fn main() {
let metadata = MetadataCommand::new()
.exec()
.expect("failed to get metadata");
let dependency = metadata.packages
.iter()
.find(|package| package.name == "postgres")
.expect("failed to find dependency");
println!("{:#?}", dependency.features);
}
{
"with-time-0_3": [
"tokio-postgres/with-time-0_3",
],
"with-smol_str-01": [
"tokio-postgres/with-smol_str-01",
],
"with-chrono-0_4": [
"tokio-postgres/with-chrono-0_4",
],
"with-uuid-0_8": [
"tokio-postgres/with-uuid-0_8",
],
"with-uuid-1": [
"tokio-postgres/with-uuid-1",
],
"array-impls": [
"tokio-postgres/array-impls",
],
"with-eui48-1": [
"tokio-postgres/with-eui48-1",
],
"with-bit-vec-0_6": [
"tokio-postgres/with-bit-vec-0_6",
],
"with-geo-types-0_6": [
"tokio-postgres/with-geo-types-0_6",
],
"with-geo-types-0_7": [
"tokio-postgres/with-geo-types-0_7",
],
"with-eui48-0_4": [
"tokio-postgres/with-eui48-0_4",
],
"with-serde_json-1": [
"tokio-postgres/with-serde_json-1",
],
"with-time-0_2": [
"tokio-postgres/with-time-0_2",
],
}
This is how other tools like cargo add and cargo feature provide dependency and feature information.

puppet ruby gem nullpo segfault

Why the pg gem is crash when call it inside of puppet environment but not when are called in the pure environment ? What I've missed ?
Server: Ubuntu 18.04 latest
Puppet 5.5 masterless
postgres 10
$ /opt/puppetlabs/puppet/bin/gem install pg
Fetching: pg-1.1.4.gem (100%)
Building native extensions. This could take a while...
Successfully installed pg-1.1.4
Parsing documentation for pg-1.1.4
Installing ri documentation for pg-1.1.4
Done installing documentation for pg after 0 seconds
1 gem installed
$ run-puppet
/opt/puppetlabs/puppet/lib/ruby/gems/2.4.0/gems/pg-1.1.4/lib/pg.rb:56: [BUG] Segmentation fault at > 0x0000000000000000
ruby 2.4.9p362 (2019-10-02 revision 67824) [x86_64-linux]
Similiar code snippet what actually test connection between ruby env and host services:
require ('pg')
conn = PG.connect( dbname: "puppetdb", password: 'password2' , host: 'localhost', user: 'user2' )
conn.close()
#
Puppet::Functions.create_function(:lookup_ssl) do
begin
require ('pg')
rescue LoadError
raise Puppet::DataBinding::LookupError, "Error loading pq gem library."
end
dispatch :up do
param 'Hash', :options
param 'Hash', :search
end
def up(options, search)
data = [] #
conn = PG.connect( dbname: options['database'], password: options['password'], host: options['host'], user: options['user'] )
conn.close()
return data
end
end
My friend has this issue and he resolved by using Postgres 9.6, reinstall pg by using cmd:
gem install pg -- --with-pg-config=POSTGRES_PATH/9.6/bin/pg_config

Resources