multiwoven-integrations 0.34.19 → 0.34.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cd1d108a22615bf693a33e70ab969742427dfe1f6af6f8d6d35e9a6fda301d5
4
- data.tar.gz: 226cf6853b8f6c211937336ffb1551a02436dfaf7142a3e217044799b5108797
3
+ metadata.gz: fc1a7c208f3183e532a2eb7d7e71db0a19fe1fc8c559d27036f9d6bf147eaf10
4
+ data.tar.gz: e5ebf0e7895beaf61fe4cfaa4d41b414e9315c0c26725c3cb4a91cb4c1cad3eb
5
5
  SHA512:
6
- metadata.gz: 9111231a712e0be82ed18bb6b283bcbed0a676ea5dafe2179f69cd80d70a68b799c873792d6d6c1b7b5e7b892411a4f7f5c8664075afc695557ef9245001f8e0
7
- data.tar.gz: '084dadfad1d504fe10a896e6cf20d4ebbac8b8a492675dda566b79d641d03b307c06dbae903d757b25a2596652727693efb52cd99c274e60cc676d590d5e3dd9'
6
+ metadata.gz: 3abb6be05b20a2d7bbdbcff69a59fb29e62129286f8473552080fbcff421b12f720ce089831b4a8c218c4003b788de49982207d2dc86d7c2ee8b7cdc8764588e
7
+ data.tar.gz: 7c3d59f96b267950075467c07228e2ed157eabc2a5365e584ad5a7fbd2a6057a58be2f82a64ac6d760f2735a21b0f59bdba1489106ab86fad3c9977f560ff29b
@@ -6,6 +6,8 @@ module Multiwoven::Integrations::Destination
6
6
  module Postgresql
7
7
  include Multiwoven::Integrations::Core
8
8
  class Client < DestinationConnector
9
+ MAX_CHUNK_SIZE = 10_000
10
+
9
11
  def check_connection(connection_config)
10
12
  connection_config = connection_config.with_indifferent_access
11
13
  create_connection(connection_config)
@@ -50,30 +52,34 @@ module Multiwoven::Integrations::Destination
50
52
  raw_table = sync_config.stream.name
51
53
  table_name = qualify_table(connection_config[:schema], raw_table)
52
54
  primary_key = sync_config.model.primary_key
53
- log_message_array = []
54
55
  db = create_connection(connection_config)
55
56
 
56
57
  write_success = 0
57
58
  write_failure = 0
59
+ log_message_array = []
58
60
 
59
- records.each do |record|
60
- query = Multiwoven::Integrations::Core::QueryBuilder.perform(action, table_name, record, primary_key)
61
- logger.debug("POSTGRESQL:WRITE:QUERY query = #{query} sync_id = #{sync_config.sync_id} sync_run_id = #{sync_config.sync_run_id}")
62
- begin
63
- response = db.exec(query)
61
+ records.each_slice(MAX_CHUNK_SIZE) do |chunk|
62
+ bulk_write(db, table_name, chunk, primary_key, action)
63
+ write_success += chunk.size
64
+ log_message_array << log_request_response("info", "bulk_#{action}", "#{chunk.size} rows")
65
+ rescue StandardError => e
66
+ logger.warn("POSTGRESQL:BULK_WRITE:FALLBACK chunk_size=#{chunk.size} error=#{e.message}")
67
+ chunk.each do |record|
68
+ response = bulk_write(db, table_name, [record], primary_key, action)
64
69
  write_success += 1
65
- log_message_array << log_request_response("info", query, response)
66
- rescue StandardError => e
67
- handle_exception(e, {
70
+ log_message_array << log_request_response("info", "fallback_#{action}", response)
71
+ rescue StandardError => individual_error
72
+ handle_exception(individual_error, {
68
73
  context: "POSTGRESQL:RECORD:WRITE:EXCEPTION",
69
74
  type: "error",
70
75
  sync_id: sync_config.sync_id,
71
76
  sync_run_id: sync_config.sync_run_id
72
77
  })
73
78
  write_failure += 1
74
- log_message_array << log_request_response("error", query, e.message)
79
+ log_message_array << log_request_response("error", "fallback_#{action}", individual_error.message)
75
80
  end
76
81
  end
82
+
77
83
  tracking_message(write_success, write_failure, log_message_array)
78
84
  rescue StandardError => e
79
85
  handle_exception(e, {
@@ -82,10 +88,48 @@ module Multiwoven::Integrations::Destination
82
88
  sync_id: sync_config.sync_id,
83
89
  sync_run_id: sync_config.sync_run_id
84
90
  })
91
+ ensure
92
+ db&.close
85
93
  end
86
94
 
87
95
  private
88
96
 
97
+ def bulk_write(db, table_name, records, primary_key, action)
98
+ return if records.empty?
99
+
100
+ columns = records.flat_map(&:keys).uniq
101
+ col_list = columns.map { |c| quote_ident(c) }.join(", ")
102
+
103
+ values_clauses = records.map do |record|
104
+ vals = columns.map { |col| escape_value(db, record[col]) }
105
+ "(#{vals.join(", ")})"
106
+ end
107
+
108
+ sql = "INSERT INTO #{table_name} (#{col_list}) VALUES #{values_clauses.join(", ")}"
109
+ sql += build_upsert_clause(columns, primary_key) if action.to_s == "destination_update"
110
+ db.exec(sql)
111
+ end
112
+
113
+ def build_upsert_clause(columns, primary_key)
114
+ return "" unless primary_key.present?
115
+
116
+ update_cols = columns.reject { |c| c.to_s == primary_key.to_s }
117
+ return " ON CONFLICT (#{quote_ident(primary_key)}) DO NOTHING" if update_cols.empty?
118
+
119
+ set_clause = update_cols.map { |c| "#{quote_ident(c)} = EXCLUDED.#{quote_ident(c)}" }.join(", ")
120
+ " ON CONFLICT (#{quote_ident(primary_key)}) DO UPDATE SET #{set_clause}"
121
+ end
122
+
123
+ def escape_value(db, value)
124
+ return "NULL" if value.nil?
125
+
126
+ "'#{db.escape_string(value.to_s)}'"
127
+ end
128
+
129
+ def quote_ident(name)
130
+ PG::Connection.quote_ident(name.to_s)
131
+ end
132
+
89
133
  def query(connection, query)
90
134
  connection.exec(query) do |result|
91
135
  result.map do |row|
@@ -108,7 +152,13 @@ module Multiwoven::Integrations::Destination
108
152
 
109
153
  def create_streams(records)
110
154
  group_by_table(records).map do |r|
111
- Multiwoven::Integrations::Protocol::Stream.new(name: r[:tablename], action: StreamAction["fetch"], json_schema: convert_to_json_schema(r[:columns]))
155
+ Multiwoven::Integrations::Protocol::Stream.new(
156
+ name: r[:tablename],
157
+ action: StreamAction["fetch"],
158
+ json_schema: convert_to_json_schema(r[:columns]),
159
+ batch_support: true,
160
+ batch_size: MAX_CHUNK_SIZE
161
+ )
112
162
  end
113
163
  end
114
164
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.34.19"
5
+ VERSION = "0.34.20"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multiwoven-integrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.34.19
4
+ version: 0.34.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Subin T P
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-24 00:00:00.000000000 Z
11
+ date: 2026-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport