• Home
  • About Us
  • Contact Us
  • Disclaimer
  • Privacy Policy
Monday, August 17, 2026
newsaiworld
  • Home
  • Artificial Intelligence
  • ChatGPT
  • Data Science
  • Machine Learning
  • Crypto Coins
  • Contact Us
No Result
View All Result
  • Home
  • Artificial Intelligence
  • ChatGPT
  • Data Science
  • Machine Learning
  • Crypto Coins
  • Contact Us
No Result
View All Result
Morning News
No Result
View All Result
Home Machine Learning

Working SQL Concurrently Throughout Three Distant DuckDB Servers with Quack

Admin by Admin
August 17, 2026
in Machine Learning
0
Exec b645139d 72cc 456b 9ad9 a810bca8e5e0.jpg
0
SHARES
0
VIEWS
Share on FacebookShare on Twitter

READ ALSO

Mathematical Experiments Are Changing into Plentiful By way of Human-Machine Teaming

A Day within the Lifetime of a Knowledge Scientist in 2026


, the great people at DuckDB launched a database communication protocol referred to as Quack. Its most important purpose was to permit DuckDB databases held on completely different servers to speak with one another over HTTP in a shopper/server association and permit them to learn and write every others knowledge.

In different phrases, utilizing the Quack protocol, DuckDB sitting on server A might now question or write to a DuckDB database on distant server B. This may sound a bit like distributed knowledge processing, but it surely’s not and the DuckDB group was at pains to emphasise that Quack doesn’t facilitate distributed question processing.

Even so, I used to be intrigued and will see many makes use of for Quack. Specifically, I used to be to search out out if it was doable to fireside off parallel SQL statements on every server and have the outputs of every question gathered and made accessible to be used or show on a coordinating server.

Word that this can be a completely different proposition from merely becoming a member of tables throughout completely different servers in a single SQL assertion. DuckDB can do this by attaching a database from server B to server A, for instance, then operating SQL on server A that may learn knowledge on server B as if it have been native.

So, to analyze the concurrent reads and writes that Quack ought to allow, I created a GitHub repo referred to as cluster-duck, and no, it’s not a distributed DuckDB cluster. It’s only a comedian tackle the frequent, impolite expression you most likely already know. However it’ll allow you to do concurrent reads and writes on distant DuckDB databases.

Word: Earlier than continuing I’d wish to state that I’ve no affiliation or business asscociation with any of the merchandise, methods or their creators which are talked about on this article.

The arrange

To check Quack out, I arrange 3 AWS EC2 servers by way of a CloudFormation stack. Every server holds one DuckDB database with one of many servers additionally appearing as a coordinator node. You’ll find the CF stack on my GitHub repo.

From AWS CloudShell (or regionally in case you have the AWS CLI put in), with the repository checked out, deploy the stack with:

aws cloudformation deploy 
--region us-east-2 
--stack-name cluster-duck-test-v2 
--template-file python-reference/infra/aws/cluster-duck-3-node.yaml 
--capabilities CAPABILITY_NAMED_IAM

For all three EC2 servers, the next was put in,

  • Amazon Linux 2023 ARM64.
  • A 512 MB swap file.
  • Python 3.12 and pip.
  • A Python digital setting at: /choose/cluster-duck-venv
  • duckdb==1.5.5
  • boto3
  • DuckDB 1.5.5 ARM64 CLI at: /usr/native/bin/duckdb
  • The official DuckDB Quack extension, loaded by the employee course of.
  • The Quack server program at: /choose/cluster-duck/quack_server.py
  • The employee database at: /var/lib/cluster-duck/employee.duckdb
  • A systemd service named: cluster-duck-quack.service
  • An computerized shutdown timer, 4 hours by default.

Quack listens on port 9494 by default. Its authentication token is retrieved from an encrypted SSM Parameter Retailer parameter.

The Python supply is copied onto all three servers as a result of they share the identical CloudFormation launch template. Nevertheless, the coordinator is barely made executable as a command on one server – normally employee 1.

These information are put in on each server:

/choose/cluster-duck/quack_server.py
/choose/cluster-duck/seed_related_data.py
/choose/cluster-duck/related_cluster_sql.py
/choose/cluster-duck/cluster_duck/sql_api.py

The coordination server (Employee 1) moreover will get these command launchers:

/usr/native/bin/cluster-duck
/usr/native/bin/cluster-duck-sql

Once you run cluster-duck-sql on the coordinator, this ultimately begins:

/choose/cluster-duck-venv/bin/python
/choose/cluster-duck/related_cluster_sql.py

The Python supply is compressed and embedded instantly contained in the CloudFormation template as a Base64-encoded archive.

Throughout EC2 bootstrap, the user-data script:

  • Decodes the embedded archive.
  • Creates /choose/cluster-duck.
  • Extracts the Python information into that listing.
  • Creates the command launchers on employee 1.
  • Begins the Quack service on each employee.

Every thing wanted is contained within the CloudFormation file.

How the coordination works

Quack carries every SQL assertion to the chosen DuckDB server and returns its outcome. The half that coordinates the three calls is the Python code operating on Employee 1.

First, each — question or — query-file argument is validated as a single SQL assertion and was a QueryFragment. The fragments are labelled within the order by which they have been provided:

fragments.append(
QueryFragment(worker_id, f"query-{index}", sql)
)

The coordinator then creates one thread per fragment and a barrier with the identical variety of contributors:

barrier = threading.Barrier(len(fragments))
epoch = time.perf_counter()

def run_fragment(fragment):
    barrier.wait()
    began = time.perf_counter()
    raw_result = self.executor(fragment.worker_id, fragment.sql)
    completed = time.perf_counter()
    return {
        "start_offset_ms": (began - epoch) * 1000,
        "duration_ms": (completed - began) * 1000,
        "outcome": raw_result,
    }
with ThreadPoolExecutor(max_workers=len(fragments)) as pool:
    futures = {
        pool.submit(run_fragment, fragment): fragment
        for fragment in fragments
    }

The barrier holds the threads till each fragment is prepared, then releases them collectively. They won’t begin on exactly the identical CPU cycle as a result of regular operating-system scheduling nonetheless applies, which is why the output features a measured begin unfold.

Every fragment will get its personal native DuckDB shopper connection on the coordinator. That connection masses Quack, attaches one distant employee and sends the assertion by way of distant.question():

with duckdb.join() as connection:
    connection.execute("INSTALL quack")
    connection.execute("LOAD quack")
    connection.execute(
        f"ATTACH {endpoint} AS distant "
        f"(TYPE quack, TOKEN {token}, DISABLE_SSL true)"
    )
    cursor = connection.execute(
        f"SELECT * FROM distant.question({sql_string(sql)})"
     )
    columns = tuple(description[0] for description in cursor.description)
    return FragmentResult(columns, cursor.fetchall())

The decision to fetchall() materialises every outcome on Employee 1. The coordinator waits for all of the futures, information when each began and the way lengthy it took, after which presents the separate leads to one output. Quack is doing the distant execution and transport; the fragment building, simultaneous launch, timing and outcome assortment are all being accomplished by Python.

Creating our take a look at knowledge

Every of the three servers has a unique DuckDB database as follows.

Employee      Database file                        Generated desk
---------------------------------------------------------------
Employee 1   /var/lib/cluster-duck/employee.duckdb   gross sales
Employee 2   /var/lib/cluster-duck/employee.duckdb   clients
Employee 3   /var/lib/cluster-duck/employee.duckdb   merchandise

Every generated desk incorporates an applicable artificial knowledge set of ten million information. Listed below are the primary 5 information of every to present you a greater thought what’s in them.

query-1 (worker-1) - choose * from gross sales restrict 5

sale_id  customer_id  product_id  amount  sales_channel  payment_method  sale_status  catalogue_price  sold_unit_price  discount_pct  sale_date
-------  -----------  ----------  --------  -------------  --------------  -----------  ---------------  ---------------  ------------  ----------
1        7,920        104,730     2         retailer          bank_transfer   shipped      1,052.3          999.69           0.05          2024-01-02
2        15,839       209,459     3         market    pockets          processing   99.59            89.63            0.1           2024-01-03
3        23,758       314,188     4         phone      bill         returned     1,146.88         974.85           0.15          2024-01-04
4        31,677       418,917     5         on-line         card            cancelled    194.17           194.17           0             2024-01-05
5        39,596       523,646     1         retailer          bank_transfer   accomplished    1,241.46         1,179.39         0.05          2024-01-06

query-2 (worker-2) - choose * from clients restrict 5

customer_id  customer_code    nation  section         membership_tier  is_active  credit_limit  joined_date  last_seen_at
-----------  ---------------  -------  --------------  ---------------  ---------  ------------  -----------  -------------------
1            CUST-0000000001  US       small_business  silver           True       250.10        2015-01-02   2025-01-01 00:00:01
2            CUST-0000000002  DE       enterprise      gold             True       250.20        2015-01-03   2025-01-01 00:00:02
3            CUST-0000000003  FR       public_sector   customary         True       250.30        2015-01-04   2025-01-01 00:00:03
4            CUST-0000000004  CA       shopper        silver           True       250.40        2015-01-05   2025-01-01 00:00:04
5            CUST-0000000005  AU       small_business  gold             True       250.50        2015-01-06   2025-01-01 00:00:05

query-3 (worker-3) - choose * from merchandise restrict 5

product_id  sku             class  model    supplier_region  catalogue_price  stock_quantity  discontinued  introduced_date
----------  --------------  --------  -------  ---------------  ---------------  --------------  ------------  ---------------
1           SKU-0000000001  residence      Bramble  EU               5.01             13              False         2020-01-02
2           SKU-0000000002  backyard    Cobalt   US               5.02             26              False         2020-01-03
3           SKU-0000000003  sports activities    Dove     APAC             5.03             39              False         2020-01-04
4           SKU-0000000004  clothes  Elm      UK               5.04             52              False         2020-01-05
5           SKU-0000000005  meals      Aster    EU               5.05             65              False         2020-01-06

The information is generated instantly inside every DuckDB database through the first EC2 bootstrap. It isn’t uploaded out of your pc or copied between servers. Every server follows this sequence.

1. Decide which employee it is

CloudFormation offers each EC2 occasion a WorkerIndex tag:

Employee 1 → WorkerIndex=1
Employee 2 → WorkerIndex=2
Employee 3 → WorkerIndex=3
The bootstrap script reads that tag by way of the EC2 Occasion Metadata Service:
WORKER_INDEX=$(curl -fsS 
  -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" 
  http://169.254.169.254/newest/meta-data/tags/occasion/WorkerIndex)

2. Run the data-generation program

CloudFormation installs this program on each server:

/choose/cluster-duck/seed_related_data.py

It then runs:

/choose/cluster-duck-venv/bin/python /choose/cluster-duck/seed_related_data.py 
--worker "$WORKER_INDEX" 
--rows 10000000

The row depend comes from the CloudFormation RowCount parameter, which defaults to 10 million.

3. Open the employee’s DuckDB file

This system opens:

/var/lib/cluster-duck/employee.duckdb

which incorporates this code.

with duckdb.join(str(args.database)) as connection:
    connection.execute(
        CREATE_SQL[args.worker],
        {"row_count": args.rows},
    )

Each server makes use of the identical database filename, but it surely’s a unique file on a unique EC2 occasion.

Accessing your coordinator terminal window

We wish to run some demos, and for that you just want to have the ability to entry the CLI terminal of your coordinator EC2 server. To try this open the AWS console and go to the EC2 console. You’ll see a display screen like this.

Click on the Occasion ID that corresponds to your coordinator occasion. On the subsequent display screen there can be a Join button in direction of the highest proper nook. Click on that. You’ll see this display screen

Be sure you’ve chosen the SSM Session Supervisor radio button, then click on the Join button on the backside proper of the display screen. That ought to provide you with entry to the CLI terminal window like this,

Examples

Within the following examples, to make issues as clear as doable I exploit the uncooked SQL textual content within the code snippets, nevertheless it’s additionally doable to retailer the SQL in separate information and use these information as enter. For instance,

[root@ip-10-42-0-10 ~]# cluster-duck-sql 
  --query-file "worker-1=/root/cluster-duck-sql/gross sales.sql" 
  --query-file "worker-2=/root/cluster-duck-sql/clients.sql" 
  --query-file "worker-3=/root/cluster-duck-sql/merchandise.sql"

1. Working some easy SQL statements

Within the terminal CLI, sort within the following code.

sh-5.2$ sudo -i
[root@ip-10-42-0-10 ~]# cluster-duck-sql 
  --query "worker-1=SELECT sale_status, COUNT(*) FROM gross sales GROUP BY sale_status ORDER BY sale_status" 
  --query "worker-2=SELECT nation, COUNT(*) FROM clients GROUP BY nation ORDER BY nation" 
  --query "worker-3=SELECT class, COUNT(*) FROM merchandise GROUP BY class ORDER BY class"


# output
#
Concurrent distant queries
employee    desk    start_offset_ms  duration_seconds
--------  -------  ---------------  ----------------
worker-1  query-1  0.996            0.572
worker-2  query-2  5.514            0.572
worker-3  query-3  0.738            0.54
Begin unfold: 4.776 ms
query-1 (worker-1)
sale_status  count_star()
-----------  ------------
cancelled    2,000,000
accomplished    2,000,000
processing   2,000,000
returned     2,000,000
shipped      2,000,000
query-2 (worker-2)
nation  count_star()
-------  ------------
AU       1,666,666
CA       1,666,667
DE       1,666,667
FR       1,666,667
UK       1,666,666
US       1,666,667
query-3 (worker-3)
class     count_star()
-----------  ------------
clothes     1,666,667
electronics  1,666,666
meals         1,666,666
backyard       1,666,667
residence         1,666,667
sports activities       1,666,667

2. Some advanced SQL (I’ve minimize out among the output to save lots of area)

[root@ip-10-42-0-10 ~]# time cluster-duck-sql 
  --query "worker-1=WITH every day AS (
      SELECT
          sale_date,
          sales_channel,
          payment_method,
          sale_status,
          COUNT(*) AS transaction_count,
          SUM(amount) AS items,
          SUM(amount * sold_unit_price) AS income,
          AVG(discount_pct) AS average_discount,
          QUANTILE_CONT(sold_unit_price, 0.50) AS median_price,
          QUANTILE_CONT(sold_unit_price, 0.95) AS p95_price
      FROM gross sales
      GROUP BY ALL
  ),
  analysed AS (
      SELECT
          *,
          SUM(income) OVER (
              PARTITION BY sales_channel
              ORDER BY sale_date
              ROWS BETWEEN 29 PRECEDING AND CURRENT ROW
          ) AS rolling_30_row_revenue,
          RANK() OVER (
              PARTITION BY sale_date
              ORDER BY income DESC
          ) AS daily_revenue_rank
      FROM every day
  )
  SELECT *
  FROM analysed
  WHERE daily_revenue_rank <= 3
  ORDER BY sale_date DESC, daily_revenue_rank
  LIMIT 100" 
  --query "worker-2=WITH customer_groups AS (
      SELECT
          nation,
          section,
          membership_tier,
          is_active,
          YEAR(joined_date) AS joined_year,
          CASE
              WHEN credit_limit < 2500 THEN 'under_2500'
              WHEN credit_limit < 5000 THEN '2500_to_4999'
              WHEN credit_limit < 7500 THEN '5000_to_7499'
              ELSE '7500_plus'
          END AS credit_band,
          COUNT(*) AS customer_count,
          AVG(credit_limit) AS average_credit_limit,
          STDDEV_POP(credit_limit) AS credit_limit_stddev,
          QUANTILE_CONT(credit_limit, 0.50) AS median_credit_limit,
          QUANTILE_CONT(credit_limit, 0.95) AS p95_credit_limit,
          MIN(joined_date) AS first_joined,
          MAX(last_seen_at) AS most_recent_activity
      FROM clients
      GROUP BY ALL
  ),
  ranked AS (
      SELECT
          *,
          SUM(customer_count) OVER (
              PARTITION BY nation
          ) AS country_total,
          RANK() OVER (
              PARTITION BY nation
              ORDER BY customer_count DESC
          ) AS group_rank
      FROM customer_groups
  )
  SELECT
      *,
      ROUND(100.0 * customer_count / country_total, 2) AS percentage_of_country
  FROM ranked
  WHERE group_rank <= 10
  ORDER BY nation, group_rank
  LIMIT 100" 
  --query "worker-3=WITH inventory_groups AS (
      SELECT
          class,
          model,
          supplier_region,
          discontinued,
          YEAR(introduced_date) AS introduced_year,
          COUNT(*) AS product_count,
          SUM(stock_quantity) AS stock_units,
          SUM(stock_quantity * catalogue_price) AS inventory_value,
          AVG(catalogue_price) AS average_price,
          STDDEV_POP(catalogue_price) AS price_stddev,
          QUANTILE_CONT(catalogue_price, 0.50) AS median_price,
          QUANTILE_CONT(catalogue_price, 0.95) AS p95_price
      FROM merchandise
      GROUP BY ALL
  ),
  ranked AS (
      SELECT
          *,
          SUM(inventory_value) OVER (
              PARTITION BY class
          ) AS category_inventory_value,
          RANK() OVER (
              PARTITION BY class
              ORDER BY inventory_value DESC
          ) AS inventory_rank
      FROM inventory_groups
  )
  SELECT
      *,
      ROUND(
          100.0 * inventory_value / category_inventory_value,
          2
      ) AS percentage_of_category_value
  FROM ranked
  WHERE inventory_rank <= 10
  ORDER BY class, inventory_rank
  LIMIT 100"



#
# Output
Concurrent distant queries
employee    desk    start_offset_ms  duration_seconds
--------  -------  ---------------  ----------------
worker-1  query-1  1.673            15.491
worker-2  query-2  1.84             4.045
worker-3  query-3  1.42             4.045
Begin unfold: 0.420 ms

query-1 (worker-1)
sale_date   sales_channel  payment_method  sale_status  transaction_count  items   income        average_discount  median_price  p95_price  rolling_30_row_revenue  daily_revenue_rank
----------  -------------  --------------  -----------  -----------------  ------  -------------  ----------------  ------------  ---------  ----------------------  ------------------
2025-12-30  retailer          bank_transfer   cancelled    6,849              34,245  32,722,337.65  0.05              955.72        1,810.568  588,590,148.25          1
...
...
...
2025-11-11  market    pockets          accomplished    6,849              6,849   6,199,619.04   0.1               905.5         1,714.096  557,458,798.88          2

query-2 (worker-2)
nation  section         membership_tier  is_active  joined_year  credit_band  customer_count  average_credit_limit  credit_limit_stddev  median_credit_limit  p95_credit_limit  first_joined  most_recent_activity  country_total  group_rank  percentage_of_country
-------  --------------  ---------------  ---------  -----------  -----------  --------------  --------------------  -------------------  -------------------  ----------------  ------------  --------------------  -------------  ----------  ---------------------
AU       small_business  gold             True       2,017        7500_plus    21,659          8,874.317             794.81               8878.10              10115.70          2017-01-01    2025-04-26 17:20:41   1,666,666      1  1.3
AU       public_sector   gold             True       2,017        7500_plus    21,649          8,873.263             794.868              8876.70              10114.30          2017-01-01    2025-04-26 17:20:35   1,666,666      2  1.3
...
...
...
US       small_business  silver           True       2,022        7500_plus    21,605          8,876.671             792.939              8873.30              10110.10          2022-01-01    2025-04-26 17:46:37   1,666,667      10  1.3

query-3 (worker-3)
class     model    supplier_region  discontinued  introduced_year  product_count  stock_units  inventory_value  average_price  price_stddev  median_price  p95_price  category_inventory_value  inventory_rank  percentage_of_category_value
-----------  -------  ---------------  ------------  ---------------  -------------  -----------  ---------------  -------------  ------------  ------------  ---------  ------------------------  --------------  ----------------------------
clothes     Aster    US               False         2,020            33,457         83,743,010   84191813103.00   1,004.833      577.373       1004.90       1904.90    4188462626332.28          1               2.01
clothes     Aster    UK               False         2,020            33,458         83,228,180   83715767600.00   1,005.04       577.369       1005.20       1905.42    4188462626332.28          2               2
...
...
...
residence         Dove     APAC             False         2,024            32,999         82,797,401   83251410292.23   1,004.931      577.217       1005.23       1904.83    4190160306717.15          8               1.99
residence         Dove     APAC             False         2,020            33,008         82,760,072   83227553226.96   1,005.078      577.503       1005.33       1905.43    4190160306717.15          9               1.99
...
...
sports activities       Elm      EU               False         2,020            33,006         82,731,982   83205651717.58   1,005.109      577.373       1005.19       1905.44    4190156973777.15          9               1.99
sports activities       Bramble  EU               False         2,020            33,005         82,700,285   83167881906.05   1,004.964      577.415       1005.21       1905.36    4190156973777.15          10              1.98
actual    0m17.664s
consumer    0m1.643s
sys     0m0.389s
[root@ip-10-42-0-10 ~]#

3. Concurrent writes/reads

To point out this we’ll concurrently write 20 new information into our gross sales desk and hearth off three reads. Every learn sees a constant snapshot containing whichever impartial insert transactions had dedicated earlier than that learn started. It could subsequently see none, some or the entire new rows, but it surely is not going to see half of a person insert or a outcome that modifications whereas the SELECT is being scanned.

Each insert on this instance is a separate autocommit transaction. If 19 inserts succeed and one fails, the 19 profitable writes stay dedicated. There isn’t any distributed transaction and Cluster-Duck doesn’t roll profitable statements again.

To make the demonstration repeatable, first take away any rows left by any earlier runs:

[root@ip-10-42-0-10 ~]# cluster-duck-sql 
  --allow-write 
  --query "worker-1=DELETE FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020"

Word additionally that in an effort to make modifications to knowledge in a database we must always provide the — allow-write argument.

Now run the 20 inserts and three reads collectively. To see what SQL is run for every of the question labels within the output we will use the — show-sql argument.

[root@ip-10-42-0-10 ~]# cluster-duck-sql 
  --show-sql 
  --allow-write 
  --query "worker-1=INSERT INTO gross sales VALUES (30000001,1,1,1,'on-line','card','accomplished',100.00,100.00,0.0,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000002,2,2,2,'retailer','bank_transfer','processing',110.00,104.50,0.05,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000003,3,3,3,'market','pockets','shipped',120.00,108.00,0.10,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000004,4,4,4,'phone','bill','accomplished',130.00,110.50,0.15,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000005,5,5,5,'on-line','card','processing',140.00,112.00,0.20,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000006,6,6,1,'retailer','bank_transfer','shipped',150.00,150.00,0.0,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000007,7,7,2,'market','pockets','accomplished',160.00,152.00,0.05,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000008,8,8,3,'phone','bill','processing',170.00,153.00,0.10,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000009,9,9,4,'on-line','card','shipped',180.00,153.00,0.15,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000010,10,10,5,'retailer','bank_transfer','accomplished',190.00,152.00,0.20,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000011,11,11,1,'market','pockets','processing',200.00,200.00,0.0,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000012,12,12,2,'phone','bill','shipped',210.00,199.50,0.05,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000013,13,13,3,'on-line','card','accomplished',220.00,198.00,0.10,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000014,14,14,4,'retailer','bank_transfer','processing',230.00,195.50,0.15,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000015,15,15,5,'market','pockets','shipped',240.00,192.00,0.20,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000016,16,16,1,'phone','bill','accomplished',250.00,250.00,0.0,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000017,17,17,2,'on-line','card','processing',260.00,247.00,0.05,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000018,18,18,3,'retailer','bank_transfer','shipped',270.00,243.00,0.10,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000019,19,19,4,'market','pockets','accomplished',280.00,238.00,0.15,DATE '2026-08-09')" 
  --query "worker-1=INSERT INTO gross sales VALUES (30000020,20,20,5,'phone','bill','processing',290.00,232.00,0.20,DATE '2026-08-09')" 
  --query "worker-1=SELECT COUNT(*) AS visible_rows FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020" 
  --query "worker-1=SELECT COUNT(*) AS visible_rows FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020" 
  --query "worker-1=SELECT COUNT(*) AS visible_rows FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020"



#
# Output

Concurrent distant queries
employee    desk     start_offset_ms  duration_seconds
--------  --------  ---------------  ----------------
worker-1  query-19  77.923           3.648
worker-1  query-11  26.242           3.705
worker-1  query-23  4.64             3.727
worker-1  query-5   8.747            3.691
worker-1  query-1   4.832            3.744
worker-1  query-2   5.243            3.903
worker-1  query-15  113.315          3.823
worker-1  query-6   15.126           3.923
worker-1  query-22  84.907           3.853
worker-1  query-14  35.127           3.907
worker-1  query-18  71.325           3.871
worker-1  query-21  56.68            3.888
worker-1  query-12  28.05            3.936
worker-1  query-7   15.349           3.949
worker-1  query-20  89.082           3.875
worker-1  query-10  36.901           3.93
worker-1  query-16  77.02            3.895
worker-1  query-13  19.964           3.954
worker-1  query-3   5.632            3.968
worker-1  query-8   58.938           3.916
worker-1  query-9   15.774           3.959
worker-1  query-4   50.36            3.954
worker-1  query-17  84.575           3.936

Begin unfold: 108.675 ms

query-19 (worker-1)
SQL:
INSERT INTO gross sales VALUES (30000019,19,19,4,'market','pockets','accomplished',280.00,238.00,0.15,DATE '2026-08-09')

Consequence:
Depend
-----
1

query-11 (worker-1)
SQL:
INSERT INTO gross sales VALUES (30000011,11,11,1,'market','pockets','processing',200.00,200.00,0.0,DATE '2026-08-09')

Consequence:
Depend
-----
1

query-23 (worker-1)
SQL:
SELECT COUNT(*) AS visible_rows FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020

Consequence:
visible_rows
------------
4
...
...
query-22 (worker-1)
SQL:
SELECT COUNT(*) AS visible_rows FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020

Consequence:
visible_rows
------------
16
...
...

query-18 (worker-1)
SQL:
INSERT INTO gross sales VALUES (30000018,18,18,3,'retailer','bank_transfer','shipped',270.00,243.00,0.10,DATE '2026-08-09')

Consequence:
Depend
-----
1

query-21 (worker-1)
SQL:
SELECT COUNT(*) AS visible_rows FROM gross sales WHERE sale_id BETWEEN 30000001 AND 30000020

Consequence:
visible_rows
------------
13
...
...
query-17 (worker-1)
SQL:
INSERT INTO gross sales VALUES (30000017,17,17,2,'on-line','card','processing',260.00,247.00,0.05,DATE '2026-08-09')

Consequence:
Depend
-----
1
[root@ip-10-42-0-10 ~]#

The output exhibits that by the point query-23 ran, 4 information had been inserted. By the point query-22 ran, 16 information had been inserted and for query-21, 13 new information had been written. This is sensible as we will see from the start_offset_ms timings that the order of operating for the queries was query-23, then query21, and at last query-22.

5. You may run DDL too

Create 3 new tables, one in every database , then question them.

[root@ip-10-42-0-10 ~]# cluster-duck-sql 
  --allow-write 
  --query "worker-1=CREATE OR REPLACE TABLE sales_agg AS SELECT sale_status, COUNT(*) AS sale_count FROM gross sales WHERE sale_status = 'cancelled' GROUP BY sale_status" 
  --query "worker-2=CREATE OR REPLACE TABLE customers_agg AS SELECT nation, COUNT(*) AS customer_count FROM clients WHERE nation = 'UK'  GROUP BY nation" 
  --query "worker-3=CREATE OR REPLACE TABLE products_agg AS SELECT class, COUNT(*) AS product_count FROM merchandise WHERE class = 'sports activities' GROUP BY class"

cluster-duck-sql 
  --query "worker-1=SELECT * FROM sales_agg ORDER BY sale_count DESC" 
  --query "worker-2=SELECT * FROM customers_agg ORDER BY customer_count DESC" 
  --query "worker-3=SELECT * FROM products_agg ORDER BY product_count DESC"

#
# Output

Concurrent distant queries
employee    desk    start_offset_ms  duration_seconds
--------  -------  ---------------  ----------------
worker-1  query-1  1.141            0.599
worker-2  query-2  1.033            0.603
worker-3  query-3  0.755            0.622
Begin unfold: 0.386 ms
query-1 (worker-1)
Depend
-----
1
query-2 (worker-2)
Depend
-----
1
query-3 (worker-3)
Depend
-----
1
Concurrent distant queries
employee    desk    start_offset_ms  duration_seconds
--------  -------  ---------------  ----------------
worker-1  query-1  0.925            0.464
worker-2  query-2  1.178            0.448
worker-3  query-3  0.675            0.467
Begin unfold: 0.503 ms
query-1 (worker-1)
sale_status  sale_count
-----------  ----------
cancelled    2,000,000
query-2 (worker-2)
nation  customer_count
-------  --------------
UK       1,666,666
query-3 (worker-3)
class  product_count
--------  -------------
sports activities    1,666,667

The price of all this.

The price of this set-up shouldn’t be a priority. For a begin, DuckDB and Quack are free to obtain and use. The three EC2 servers that I’m standing up are tiny t4g.nano situations. In addition to that, we now have three 8 GB gp3 volumes, public IPv4 addresses, Methods Supervisor, Parameter Retailer and a Lambda customized useful resource. The Lambda invocation is short-lived, however stays deployed till the stack is deleted. This Lambda known as TokenManagerFunction within the CloudFormation template. Its solely job is to handle the three Quack authentication tokens. It really works like this,

CloudFormation stack creation
          ↓
Invoke TokenManager Lambda
          ↓
Generate three random 64-character tokens
          ↓
Retailer them as SecureString parameters in SSM
          ↓
Return success and cease

Right here is an estimated price if we ran this entire set-up for 4 hours.

Element                     Approximate price
4 hours of EBS             $0.011
4 hours of EC2             $0.050
4 hours of public IPv4     $0.060

Whole                         $0.121

The $0.121 determine is an estimate for us-east-2, earlier than any credit or free-tier allowances, tax and data-transfer expenses. Qualifying AWS Free Tier customers might obtain some public IPv4 hours at no cost. AWS payments gp3 storage in per-second increments, with a 60-second minimal.

For peace of thoughts, although, I might at all times advise tearing down any AWS infrastructure created after you’re accomplished with it. That is simply accomplished in case you use CloudFormation by operating the next command with the AWS CLI.

aws cloudformation delete-stack 
  --region us-east-2 
  --stack-name cluster-duck-test-v2

Abstract

I created the “cluster-duck” repo to check DuckDB’s new Quack communications protocol. Quack permits DuckDB databases on completely different servers to “discuss” to one another over HTTP, and DuckDB is positioning it as an enabler of shopper/server communications between DuckDB databases. 

This improvement is probably very helpful and I used to be notably to see how nicely Quack dealt with concurrent reads and writes to and from a distant database.

In my exams, and within the instance I demonstrated, the reply appears to be it handles it fairly nicely.

The DuckDB group have acknowledged that Quack is an experimental function and really a lot a work-in-progress. To that time, you’ll be able to anticipate potential modifications to the protocol, operate names, settings and defaults, so positively don’t use Quack for any manufacturing methods.

Hopefully the ideas I’ve outlined on this article can be helpful in case you have a have to run parallel queries or different SQL statements moere typically in opposition to DuckDB databases operating on completely different servers.

I can’t assist however surprise what future plans DuckDB have for Quack. If it turns into a completely supported a part of the DuckDB eco-system I might positively see Quack supplying the transport and session dealing with to a future distributed DuckDB engine, however that’s most likely a good distance off. However, even it simply does what its able to now, Quack can be helpful in its personal proper.

That’s all from me for now. You may entry all of the code, CloudFormation template and so forth… in my GitHub repo at:

https://github.com/taupirho/cluster-duck

Extra data on DuckDB and Quack may be discovered within the DuckDB on-line documentation at this hyperlink,

https://duckdb.org/docs/present

PS I’m in the marketplace for contract work simply now. If you happen to or somebody you realize is on the lookout for an skilled knowledge engineer, both distant or primarily based in Edinburgh, UK, with abilities in AWS, AI, Python, SQL, PySpark, DuckDB, and so forth., let me know by way of LinkedIn

Tags: ConcurrentlyDuckDBRemoterunningServersSQLwithQuack

Related Posts

1hVWgrxTiXs6M3c4lGNPjdg.jpg
Machine Learning

Mathematical Experiments Are Changing into Plentiful By way of Human-Machine Teaming

August 15, 2026
Ofspace llc ZTLUNxoRaPY unsplash scaled 1.jpg
Machine Learning

A Day within the Lifetime of a Knowledge Scientist in 2026

August 14, 2026
Mika baumeister 3XjMwxUHx0Q unsplash scaled 1.jpg
Machine Learning

LangChain vs LangGraph: 4 Key Variations and When to Use Every

August 13, 2026
Jacob smith LcuBRr7pRCc unsplash scaled.jpg
Machine Learning

Utilizing a Transformer Mannequin: From Coaching to Inference

August 12, 2026
Cover 1600x900.jpg
Machine Learning

Cease Calling the First Vital Day a Win

August 11, 2026
Mlm 7 chunking strategies that decide whether your rag works feature 1.png
Machine Learning

7 Chunking Methods That Resolve Whether or not Your RAG Works

August 11, 2026

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

POPULAR NEWS

Gemini 2.0 Fash Vs Gpt 4o.webp.webp

Gemini 2.0 Flash vs GPT 4o: Which is Higher?

January 19, 2025
Chainlink Link And Cardano Ada Dominate The Crypto Coin Development Chart.jpg

Chainlink’s Run to $20 Beneficial properties Steam Amid LINK Taking the Helm because the High Creating DeFi Challenge ⋆ ZyCrypto

May 17, 2025
Image 100 1024x683.png

Easy methods to Use LLMs for Highly effective Computerized Evaluations

August 13, 2025
Blog.png

XMN is accessible for buying and selling!

October 10, 2025
0 3.png

College endowments be a part of crypto rush, boosting meme cash like Meme Index

February 10, 2025

EDITOR'S PICK

National institute of allergy and infectious diseases oc12eproeoi unsplash scaled 1.jpg

I Spent an Hour on a Information Preprocessing Process Earlier than Asking Gemini

June 24, 2026
1750422730 shiba inu sees trillions in accumulation spree by mysterious whales as 0.001 shib price beckons.jpg

How Vitalik Buterin’s Proposal to Change Ethereum’s EVM May Enhance Shiba Inu ⋆ ZyCrypto

June 20, 2025
Anthropic claude fable 5 ban security vulnerability 1.png

U.S. Authorities Kills Anthropic’s Flagship Mannequin |

June 16, 2026
TalkGraph1 Process.width 800.gif

Encoding graphs for big language fashions

August 1, 2024

About Us

Welcome to News AI World, your go-to source for the latest in artificial intelligence news and developments. Our mission is to deliver comprehensive and insightful coverage of the rapidly evolving AI landscape, keeping you informed about breakthroughs, trends, and the transformative impact of AI technologies across industries.

Categories

  • Artificial Intelligence
  • ChatGPT
  • Crypto Coins
  • Data Science
  • Machine Learning

Recent Posts

  • Working SQL Concurrently Throughout Three Distant DuckDB Servers with Quack
  • Information of 54K Pockets Customers Leaked, Readability Odds Simply 10%: Hodler’s Digest, Aug. 16
  • 5 Course of Errors to Keep away from
  • Home
  • About Us
  • Contact Us
  • Disclaimer
  • Privacy Policy

© 2024 Newsaiworld.com. All rights reserved.

No Result
View All Result
  • Home
  • Artificial Intelligence
  • ChatGPT
  • Data Science
  • Machine Learning
  • Crypto Coins
  • Contact Us

© 2024 Newsaiworld.com. All rights reserved.

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?