From 96a78211f6ce82fae642c29a534a440d6a155be8 Mon Sep 17 00:00:00 2001 From: jiajingbin Date: Fri, 26 Aug 2022 19:03:23 +0800 Subject: [PATCH 1/2] test: add java fast_write_example for 3.0 --- .../example/highvolume/DataBaseMonitor.java | 63 ++++++ .../example/highvolume/FastWriteExample.java | 70 ++++++ .../example/highvolume/MockDataSource.java | 53 +++++ .../com/taos/example/highvolume/ReadTask.java | 58 +++++ .../taos/example/highvolume/SQLWriter.java | 205 ++++++++++++++++++ .../taos/example/highvolume/StmtWriter.java | 4 + .../taos/example/highvolume/WriteTask.java | 58 +++++ 7 files changed, 511 insertions(+) create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java new file mode 100644 index 0000000000..04b149a4b9 --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java @@ -0,0 +1,63 @@ +package com.taos.example.highvolume; + +import java.sql.*; + +/** + * Prepare target database. + * Count total records in database periodically so that we can estimate the writing speed. + */ +public class DataBaseMonitor { + private Connection conn; + private Statement stmt; + + public DataBaseMonitor init() throws SQLException { + if (conn == null) { + String jdbcURL = System.getenv("TDENGINE_JDBC_URL"); + conn = DriverManager.getConnection(jdbcURL); + stmt = conn.createStatement(); + } + return this; + } + + public void close() { + try { + stmt.close(); + } catch (SQLException e) { + } + try { + conn.close(); + } catch (SQLException e) { + } + } + + public void prepareDatabase() throws SQLException { + stmt.execute("DROP DATABASE IF EXISTS test"); + stmt.execute("CREATE DATABASE test"); + stmt.execute("CREATE STABLE test.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); + } + + public Long count() throws SQLException { + if (!stmt.isClosed()) { + ResultSet result = stmt.executeQuery("SELECT count(*) from test.meters"); + result.next(); + return result.getLong(1); + } + return null; + } + + /** + * show test.stables; + * + * name | created_time | columns | tags | tables | + * ============================================================================================ + * meters | 2022-07-20 08:39:30.902 | 4 | 2 | 620000 | + */ + public Long getTableCount() throws SQLException { + if (!stmt.isClosed()) { + ResultSet result = stmt.executeQuery("show test.stables"); + result.next(); + return result.getLong(5); + } + return null; + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java new file mode 100644 index 0000000000..41b59551ca --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java @@ -0,0 +1,70 @@ +package com.taos.example.highvolume; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + + +public class FastWriteExample { + final static Logger logger = LoggerFactory.getLogger(FastWriteExample.class); + + final static int taskQueueCapacity = 1000000; + final static List> taskQueues = new ArrayList<>(); + final static List readTasks = new ArrayList<>(); + final static List writeTasks = new ArrayList<>(); + final static DataBaseMonitor databaseMonitor = new DataBaseMonitor(); + + public static void stopAll() { + logger.info("shutting down"); + readTasks.forEach(task -> task.stop()); + writeTasks.forEach(task -> task.stop()); + databaseMonitor.close(); + } + + public static void main(String[] args) throws InterruptedException, SQLException { + int readTaskCount = args.length > 0 ? Integer.parseInt(args[0]) : 1; + int writeTaskCount = args.length > 1 ? Integer.parseInt(args[1]) : 3; + int tableCount = args.length > 2 ? Integer.parseInt(args[2]) : 1000; + int maxBatchSize = args.length > 3 ? Integer.parseInt(args[3]) : 3000; + + logger.info("readTaskCount={}, writeTaskCount={} tableCount={} maxBatchSize={}", + readTaskCount, writeTaskCount, tableCount, maxBatchSize); + + databaseMonitor.init().prepareDatabase(); + + // Create task queues, whiting tasks and start writing threads. + for (int i = 0; i < writeTaskCount; ++i) { + BlockingQueue queue = new ArrayBlockingQueue<>(taskQueueCapacity); + taskQueues.add(queue); + WriteTask task = new WriteTask(queue, maxBatchSize); + Thread t = new Thread(task); + t.setName("WriteThread-" + i); + t.start(); + } + + // create reading tasks and start reading threads + int tableCountPerTask = tableCount / readTaskCount; + for (int i = 0; i < readTaskCount; ++i) { + ReadTask task = new ReadTask(i, taskQueues, tableCountPerTask); + Thread t = new Thread(task); + t.setName("ReadThread-" + i); + t.start(); + } + + Runtime.getRuntime().addShutdownHook(new Thread(FastWriteExample::stopAll)); + + long lastCount = 0; + while (true) { + Thread.sleep(10000); + long numberOfTable = databaseMonitor.getTableCount(); + long count = databaseMonitor.count(); + logger.info("numberOfTable={} count={} speed={}", numberOfTable, count, (count - lastCount) / 10); + lastCount = count; + } + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java new file mode 100644 index 0000000000..6fe83f002e --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java @@ -0,0 +1,53 @@ +package com.taos.example.highvolume; + +import java.util.Iterator; + +/** + * Generate test data + */ +class MockDataSource implements Iterator { + private String tbNamePrefix; + private int tableCount; + private long maxRowsPerTable = 1000000000L; + + // 100 milliseconds between two neighbouring rows. + long startMs = System.currentTimeMillis() - maxRowsPerTable * 100; + private int currentRow = 0; + private int currentTbId = -1; + + // mock values + String[] location = {"LosAngeles", "SanDiego", "Hollywood", "Compton", "San Francisco"}; + float[] current = {8.8f, 10.7f, 9.9f, 8.9f, 9.4f}; + int[] voltage = {119, 116, 111, 113, 118}; + float[] phase = {0.32f, 0.34f, 0.33f, 0.329f, 0.141f}; + + public MockDataSource(String tbNamePrefix, int tableCount) { + this.tbNamePrefix = tbNamePrefix; + this.tableCount = tableCount; + } + + @Override + public boolean hasNext() { + currentTbId += 1; + if (currentTbId == tableCount) { + currentTbId = 0; + currentRow += 1; + } + return currentRow < maxRowsPerTable; + } + + @Override + public String next() { + long ts = startMs + 100 * currentRow; + int groupId = currentTbId % 5 == 0 ? currentTbId / 5 : currentTbId / 5 + 1; + StringBuilder sb = new StringBuilder(tbNamePrefix + "_" + currentTbId + ","); // tbName + sb.append(ts).append(','); // ts + sb.append(current[currentRow % 5]).append(','); // current + sb.append(voltage[currentRow % 5]).append(','); // voltage + sb.append(phase[currentRow % 5]).append(','); // phase + sb.append(location[currentRow % 5]).append(','); // location + sb.append(groupId); // groupID + + return sb.toString(); + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java new file mode 100644 index 0000000000..a6fcfed1d2 --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java @@ -0,0 +1,58 @@ +package com.taos.example.highvolume; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BlockingQueue; + +class ReadTask implements Runnable { + private final static Logger logger = LoggerFactory.getLogger(ReadTask.class); + private final int taskId; + private final List> taskQueues; + private final int queueCount; + private final int tableCount; + private boolean active = true; + + public ReadTask(int readTaskId, List> queues, int tableCount) { + this.taskId = readTaskId; + this.taskQueues = queues; + this.queueCount = queues.size(); + this.tableCount = tableCount; + } + + /** + * Assign data received to different queues. + * Here we use the suffix number in table name. + * You are expected to define your own rule in practice. + * + * @param line record received + * @return which queue to use + */ + public int getQueueId(String line) { + String tbName = line.substring(0, line.indexOf(',')); // For example: tb1_101 + String suffixNumber = tbName.split("_")[1]; + return Integer.parseInt(suffixNumber) % this.queueCount; + } + + @Override + public void run() { + logger.info("started"); + Iterator it = new MockDataSource("tb" + this.taskId, tableCount); + try { + while (it.hasNext() && active) { + String line = it.next(); + int queueId = getQueueId(line); + taskQueues.get(queueId).put(line); + } + } catch (Exception e) { + logger.error("Read Task Error", e); + } + } + + public void stop() { + logger.info("stop"); + this.active = false; + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java new file mode 100644 index 0000000000..c2989acdbe --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java @@ -0,0 +1,205 @@ +package com.taos.example.highvolume; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.*; +import java.util.HashMap; +import java.util.Map; + +/** + * A helper class encapsulate the logic of writing using SQL. + *

+ * The main interfaces are two methods: + *

    + *
  1. {@link SQLWriter#processLine}, which receive raw lines from WriteTask and group them by table names.
  2. + *
  3. {@link SQLWriter#flush}, which assemble INSERT statement and execute it.
  4. + *
+ *

+ * There is a technical skill worth mentioning: we create table as needed when "table does not exist" error occur instead of creating table automatically using syntax "INSET INTO tb USING stb". + * This ensure that checking table existence is a one-time-only operation. + *

+ * + *

+ */ +public class SQLWriter { + final static Logger logger = LoggerFactory.getLogger(SQLWriter.class); + + private Connection conn; + private Statement stmt; + + /** + * current number of buffered records + */ + private int bufferedCount = 0; + /** + * Maximum number of buffered records. + * Flush action will be triggered if bufferedCount reached this value, + */ + private int maxBatchSize; + + + /** + * Maximum SQL length. + */ + private int maxSQLLength; + + /** + * Map from table name to column values. For example: + * "tb001" -> "(1648432611249,2.1,114,0.09) (1648432611250,2.2,135,0.2)" + */ + private Map tbValues = new HashMap<>(); + + /** + * Map from table name to tag values in the same order as creating stable. + * Used for creating table. + */ + private Map tbTags = new HashMap<>(); + + public SQLWriter(int maxBatchSize) { + this.maxBatchSize = maxBatchSize; + } + + + /** + * Get Database Connection + * + * @return Connection + * @throws SQLException + */ + private static Connection getConnection() throws SQLException { + String jdbcURL = System.getenv("TDENGINE_JDBC_URL"); + return DriverManager.getConnection(jdbcURL); + } + + /** + * Create Connection and Statement + * + * @throws SQLException + */ + public void init() throws SQLException { + conn = getConnection(); + stmt = conn.createStatement(); + stmt.execute("use test"); + ResultSet rs = stmt.executeQuery("show variables"); + while (rs.next()) { + String configName = rs.getString(1); + if ("maxSQLLength".equals(configName)) { + maxSQLLength = Integer.parseInt(rs.getString(2)); + logger.info("maxSQLLength={}", maxSQLLength); + } + } + } + + /** + * Convert raw data to SQL fragments, group them by table name and cache them in a HashMap. + * Trigger writing when number of buffered records reached maxBachSize. + * + * @param line raw data get from task queue in format: tbName,ts,current,voltage,phase,location,groupId + */ + public void processLine(String line) throws SQLException { + bufferedCount += 1; + int firstComma = line.indexOf(','); + String tbName = line.substring(0, firstComma); + int lastComma = line.lastIndexOf(','); + int secondLastComma = line.lastIndexOf(',', lastComma - 1); + String value = "(" + line.substring(firstComma + 1, secondLastComma) + ") "; + if (tbValues.containsKey(tbName)) { + tbValues.put(tbName, tbValues.get(tbName) + value); + } else { + tbValues.put(tbName, value); + } + if (!tbTags.containsKey(tbName)) { + String location = line.substring(secondLastComma + 1, lastComma); + String groupId = line.substring(lastComma + 1); + String tagValues = "('" + location + "'," + groupId + ')'; + tbTags.put(tbName, tagValues); + } + if (bufferedCount == maxBatchSize) { + flush(); + } + } + + + /** + * Assemble INSERT statement using buffered SQL fragments in Map {@link SQLWriter#tbValues} and execute it. + * In case of "Table does not exit" exception, create all tables in the sql and retry the sql. + */ + public void flush() throws SQLException { + StringBuilder sb = new StringBuilder("INSERT INTO "); + for (Map.Entry entry : tbValues.entrySet()) { + String tableName = entry.getKey(); + String values = entry.getValue(); + String q = tableName + " values " + values + " "; + if (sb.length() + q.length() > maxSQLLength) { + executeSQL(sb.toString()); + logger.warn("increase maxSQLLength or decrease maxBatchSize to gain better performance"); + sb = new StringBuilder("INSERT INTO "); + } + sb.append(q); + } + executeSQL(sb.toString()); + tbValues.clear(); + bufferedCount = 0; + } + + private void executeSQL(String sql) throws SQLException { + try { + stmt.executeUpdate(sql); + } catch (SQLException e) { + // convert to error code defined in taoserror.h + int errorCode = e.getErrorCode() & 0xffff; + if (errorCode == 0x362 || errorCode == 0x218) { + // Table does not exist + createTables(); + executeSQL(sql); + } else { + logger.error("Execute SQL: {}", sql); + throw e; + } + } catch (Throwable throwable) { + logger.error("Execute SQL: {}", sql); + throw throwable; + } + } + + /** + * Create tables in batch using syntax: + *

+ * CREATE TABLE [IF NOT EXISTS] tb_name1 USING stb_name TAGS (tag_value1, ...) [IF NOT EXISTS] tb_name2 USING stb_name TAGS (tag_value2, ...) ...; + *

+ */ + private void createTables() throws SQLException { + StringBuilder sb = new StringBuilder("CREATE TABLE "); + for (String tbName : tbValues.keySet()) { + String tagValues = tbTags.get(tbName); + sb.append("IF NOT EXISTS ").append(tbName).append(" USING meters TAGS ").append(tagValues).append(" "); + } + String sql = sb.toString(); + try { + stmt.executeUpdate(sql); + } catch (Throwable throwable) { + logger.error("Execute SQL: {}", sql); + throw throwable; + } + } + + public boolean hasBufferedValues() { + return bufferedCount > 0; + } + + public int getBufferedCount() { + return bufferedCount; + } + + public void close() { + try { + stmt.close(); + } catch (SQLException e) { + } + try { + conn.close(); + } catch (SQLException e) { + } + } +} \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java new file mode 100644 index 0000000000..8ade06625d --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java @@ -0,0 +1,4 @@ +package com.taos.example.highvolume; + +public class StmtWriter { +} diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java new file mode 100644 index 0000000000..de9e5463d7 --- /dev/null +++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java @@ -0,0 +1,58 @@ +package com.taos.example.highvolume; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.BlockingQueue; + +class WriteTask implements Runnable { + private final static Logger logger = LoggerFactory.getLogger(WriteTask.class); + private final int maxBatchSize; + + // the queue from which this writing task get raw data. + private final BlockingQueue queue; + + // A flag indicate whether to continue. + private boolean active = true; + + public WriteTask(BlockingQueue taskQueue, int maxBatchSize) { + this.queue = taskQueue; + this.maxBatchSize = maxBatchSize; + } + + @Override + public void run() { + logger.info("started"); + String line = null; // data getting from the queue just now. + SQLWriter writer = new SQLWriter(maxBatchSize); + try { + writer.init(); + while (active) { + line = queue.poll(); + if (line != null) { + // parse raw data and buffer the data. + writer.processLine(line); + } else if (writer.hasBufferedValues()) { + // write data immediately if no more data in the queue + writer.flush(); + } else { + // sleep a while to avoid high CPU usage if no more data in the queue and no buffered records, . + Thread.sleep(100); + } + } + if (writer.hasBufferedValues()) { + writer.flush(); + } + } catch (Exception e) { + String msg = String.format("line=%s, bufferedCount=%s", line, writer.getBufferedCount()); + logger.error(msg, e); + } finally { + writer.close(); + } + } + + public void stop() { + logger.info("stop"); + this.active = false; + } +} \ No newline at end of file From 4ff7bb654a13e260ad75dde07cd72a70c6cb81c3 Mon Sep 17 00:00:00 2001 From: jiajingbin Date: Fri, 26 Aug 2022 19:05:51 +0800 Subject: [PATCH 2/2] test: add highvolume.webp for 3.0 --- .../en/07-develop/03-insert-data/highvolume.webp | Bin 0 -> 7308 bytes .../zh/07-develop/03-insert-data/highvolume.webp | Bin 0 -> 7308 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/en/07-develop/03-insert-data/highvolume.webp create mode 100644 docs/zh/07-develop/03-insert-data/highvolume.webp diff --git a/docs/en/07-develop/03-insert-data/highvolume.webp b/docs/en/07-develop/03-insert-data/highvolume.webp new file mode 100644 index 0000000000000000000000000000000000000000..46dfc74ae3b0043c591ff930c62251da49cae7ad GIT binary patch literal 7308 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 literal 0 HcmV?d00001 diff --git a/docs/zh/07-develop/03-insert-data/highvolume.webp b/docs/zh/07-develop/03-insert-data/highvolume.webp new file mode 100644 index 0000000000000000000000000000000000000000..46dfc74ae3b0043c591ff930c62251da49cae7ad GIT binary patch literal 7308 zcmV;79CPDRNk&G58~^}UMM6+kP&iC?8~^|>J;OQx^$LQvZIhNi>vh|I9Y91(0N+-{ z@q~s&%Rp;e0%Z<1bLVc{L=nxhVdzfv5b1mZbdtw+o+)`|6nWX*#S=C0zJ@c^WFPpr zEYSaFI+j#3Ya?shaJ~$*0#6C=#yD{Yk3ZBZU2-;(swkodNj=A?n6K0l5ux$_jNnmOW3=ZyI4DN zcX!`~Pz59u6oew(=@6r>?Vr*}`lXf7)0v?w1VZ7VQV@zzjO)4{^$qM5%nj}Fp6;2u zOP(GV#@)STeGKS7BsP*1Nf8IVa}2#Om|gRu$N&F1De^wDYmsf+wryMI%eHOXwins9 z?KZpq*q!D4cKZE(-+#`wefN1}Ot!5#=00a+O`RRle@JW_Ig;edapP-SQ{KV6bwBKX8z?_!pP0EcEX=X6ou0PF$lpU?uNYWWqXoeEoJ*ErL z?p)6Z;{1BVhA4K;M3PRdNGnwk0_miz7wO0ei<|XfW-KjO++t?lg-NI=WaY}&hv_Ov zf=EL^sa1Ze&-GzC3`vqBsMISjS;l*uievBE>ZcyvZ`(t(83^2^(9A7rU#9sP%hUv| zI;a#iwP*m`p!v|W^L*T7n?zXCal1EGai8|c-eYddw`VYFC0Vn@$fhID{E_sJ9uWo-|*Q^DHA6T@kBRuVV?RB>x4CxN9J?) zZoBq2ze{1f%EVvL_&yZ-Bzgtr2RmHYu3ufuzKvD^VLd#u^GZ{T3;AMyZ>?GW=ly2C zqr?SSSOmPsBbetd!*XcS@DOb>1u_+LwUD4BN1sB%-4Fm&qtCf+%>1%;J}grczRLHx zbfczn61{{aKYUYpr1n7nZ7F`ymYuohHT)BAKL%!SdUk{x6g7HIW74G*hvpznv z-q6Fap=*e7qRTW$89TkYV#?ZDu^-cdq7qjJY6HVlA8Y{peO#s8(%Q?UHj&xQZ=&PU z#JK6#l^)Y>TapO2gU5%GrWkyzZw@8|$l z{A#NQ(uWVB3YPtLk2+i9kDoE=<>tnW)RG#YH0Q2{HV|;Tm>}jx2~b#xO(ouPpn2_~ zr&zS8in*~%axp(itLJ$@y)$WkbqDr1Y=Ng%7WnGz|7v5N`WW#>d8Fg_F9-i4!4q#a zh2SFv;Fzj^{88oW%KIxVt@wO}JLS)o+gTQ%%oC-rm-?dQ<`VA~f2i1n3j#rrwS`|V z^jE=e3mnY9BHxR7fB7ZP`P{p6EzJ3Jj+@y}X4{(e{Vb1WzMSckjO#N5xjH}qYvdS` zBu5_R$nBgTFp2Gc;y4E=L3Gj2P|(I_CE1;yF67Jp%WRu!RokYg_q+5R;)CYKj23GD zL@e7{wd>ni@%ROip{-rJY@f69v)vc$Ew_Kr!8Z>7boAP5j@LLj?(~AQ|DC_%Vw20y zuU)(5`f)cOx!vLJjQiUMuL3yd{F>l8U^PRMMB-r<9kEkLG7C-Bc~5>#+(?C!Raviu zCEDz1tL(JRtIvMV-E`i_Y4W8bxtnKE>B0r9^=7acI9U6I6eUI|*|mWf;f5cUVOM+M(Sh-=)wj)tp9L06nT7X~_At4K#?cHtHPaYLvpqz<#ER5uGtcmUp46+dCHWs`M{sc-a3g^k+4UfVe2^j=cE^Zyi+Cd-XTI`jE#TUG^i_ zN^{t9ra68+P~TIeI^WNv1F%#VZdyei5yLNq3$RvHHjU{fzrM4qsYKnQ@B2*-niNePUVH>dX9*1?`jRf-~Mp=WlV`}GIR zKM+6XcqJ;#s(F_+uphUvnnjVX$Rk0a$5tN_1_Kuyq~>JsK~F~{CCsc5%L;rGuZjAP zZ)`Y0NVWjn?NRUIh87hXf*Pc%G6~uG{PgNEErX*3i;2wSldhS)iai$e^`UffD~vR1 z>eeo_AW0_E{)k}-)+AJUky3&ATgi|E=Nv)=vcjpPCOC~R1 zG`L_GEuEK2ho%kxD9RJ@GOdb&j)acPC?va5TBrQHbHCJsJSK*V~{xL&Ew!gLO0Dfaq#mMHsdD7;{ z4kpr-hXQ3rt{WSdihpRI&I3q?E;$O>!t6rJ_*)357FV9)dnQ(hWCmO&Yk?{uF0@PI z4rIcV8iR<$g$d;&ng&-XTB1sb3vE-o1-Wpg$08zeVSL%}rbJw)Y>g@*F04!C222Xd zjzdJ^!dGS1%8BgwtqrP#xX>!aHFzmPxmOU8xUlq#u(EJwRa;aE+X{9d3AbY&r=W&3 zf)SDonc6VL-*qHRAMj7|&*5;Sv#MJ@U~4h%-y98q$&B9o-80}m?fa+__7-F4qkY;Z z5)hG2VIs|+bpd80KYx^QckvZs!$vR7=L1}6fHK@IqJ^+0PH(A_j~eppGC(%7jbK!A zIW#Fm1%nudQw(c7zN`l%TefeE(*u&gArs*7hYn15;Y zz>N`&9)uS#wkt6NZT}Z=3s8v?4TS(?PDMVy2B`->UjXnsf+&^#W#-Ms#8$o8C?SeM z25}}?i*k%sqmw@w-O6OBF<3@0E(6Eb>r#LVj_}XPaEc)`!{6I+8ltPM_CE#O6!2xh z+5p?NC_|@0d6|;KoX6Z4dlpA--J2yG?aquOjU?Fe#^oi{Y*}DZPBD~F-tK9b>w_D2 z=I#Uz$OT)0pgvXK*1cJyGo>MzCW}!^W{`cMkXhnR14b~A+dW7jDHu*MP<86lBQ?Dq zti4ym1I9_WB^N7Lm>Q)ik;=&z2DkDd5F;BlHnDSxzz5p1&IbE-z^x z#opcZ1=a*ClGWQ=K?vKwdz zj{*FZU@M?M14&HzzX%z@@WAe&W9@U>Yt5f%nV@sy@ivLr>5uAV^qY*)m^; zK!{#1!d+j{n=Za?p+&4a{=DC*vtN4g&pUsmLx9-eaRZrXP8Z*-yGPZKimq6)h<$}M zs!M=a*sRU7w5E%%Wq4v*xvEBqbEUdk{hMt%1voFMnD>@+ri-tX)IeTcAY_nNCKo9P z;=l+Hbr|jF;_LF_>8}ywBnsrfHd9Ok02$=gj$5UA4-y=WRx=&U0Nvm(W5$hFo;Z2RwCOWv&6zu& z67M+w%56Zu3gH>#y<-ok37&}#Vf)jFF23f}eZC$i0tt^BSq+~R#l$7|>(Z@PzX3yr zjT$o{E&Hn}GiJ@Nvt)UTH5;~U+qLK5PsdK2xp4Wo8+YzK?(^c+yANM}{Q3_NC~(kV zAwq@@6E6J2Zhy(KMvftgG6j)AUSHpUU`FXaR4k4>#fzPn`hvuB=tmdd^c#mvM_1KM zr2uhQO*fbcrh{o<@Sp+x`}XP8qkGpbojSH}*JfSI=FOB8<(o8WSifGK+BIubt5UgQ zg>q#}e^H`%(ISNkYfif{S!kuD^?ws(#=k1t2NH@CpCf++_+PjJ^=%q%2 zfBwFMzpE~10+2!8u*OUKsn)#Y9{5gH`nigha z%cTI);A&xzok8B7N;IN43RC@LX0Ah|IE)fyvMVhggg^)gai&lb-rL0&;+*5sU>yg6 zS%DC40LUP(dzx2%fnav*Ph|xxL8PsT4UUl+%?#owAvP-8!fUDP?9u)%z5@|80FXic zGJFRJQLXK8r-&0GKO7c^43Y)4VUipfrBSd|Z%%lxI?f&&W#y5#Q=-m-RBLLF9Y_w> zj*{yeciO^tPKyWrJ5UYfYM{FKtt!ZehEmbllXvS#1~_v)@QC@Bx;K<`_BvVzPObs+ z!I8Q*8aaEmsN(DiK}v;df(HYke?wJgFOIkhdX!mkH0A6K=RP$ZF7_n?l2wrfM^nz; z*QvNn_rUP~bRkImfUG=-rL1t(*}D=K0(DdQw#wI-0v!cO^b)Al#eRw(3u?Pcw}-vl zBERgPH8>YX{(($^Nv+O6H-}1jEh>x#g5Hkcmb3SvOT*dKKG?(@=B4g5n2L0BsN3Yk zc%sedg66jN$pvSxUnn@BwzI{FhZsZ`-@4R_mjY&le|tTQ65>;W?mlnFbU|%peiHD_ zR0im0hQm}p8J8IlpTnJE!eU3zPzpJF#mw1!9+^sZ>Q9x{eIqyI)lu*PQ2yoAZ$}A1 z$!E$qdrHsgNsmk>Q(B9|6FWWRhk!E0kB@CLIxXKy5od2`B#1T=+4xYz*-O3O%VA{Y zLk(xItTjuekd+TLoV|5N#8V(k6FT7Rjg1N61|eG$8sO|@*1w|(vNu`aJ9|~_t}KKk zC5wcj>N$HiK7fR?k+9^U@M@gB$@hr_?~Mc|iX6^fF5^z?BB99_R^%+0*p0*{J%fS7 z4h2%ftOO9CGl#QBWGHCLUNv=`J>o)RHwGV7-`QhSY+Ncmr3%X4c6;0243m83Hz3wm z8qT|8N^H_m%45$twqHbID3qz2X;;^Dhh`{2N-Mgm2FmF$90ITl|D+lrT?tXY}m9jZ;VTg5KJ?I zxGuFAfpoe(Hr1lD7Z?0@WHO9yYB5z20!n2)$O;(~P{F2!;@Yt6L9_Gjg8#kGGXU*; zK{9P}_FNgrL>?Hw48myNN}xjD+qy#-*lOrnL+w13b5)%^f&kyamj$@u?5VT$d3K+! zfkZaW;EuERGACKxvH47U5Q4tI<#~5Pcs~Hw<=QPU&k2t!0((!>dB`ihrWV)d-3fQ< z+A|>6<$CpzUtQsR=QvzC(a>u%3MjiLh3x3-QZrnfXSd9&ksIRL7jy0+bAw9{m*Bzxdze#wb;&U=- z1rM640&xa;DHZ$AXAFOiAT~FH#zI!}ZxXf0x$EiLU^PRCPqOUP;b~22EAN$|4g2L< zs`n(%KZ_c5xxNQ`)s4!f4oWl zw!q#m*Ws!m4Up&j2uhOW3g5*Dzc8}7WOegYnba!3OH)t-Tz_jy8xt=ph>hlk=@UlB zfQv)uLI?TR-uv~oYfWsZW*2_&4WI3#0R3ETfk?8fxs;adPjq;=Q`D#+1kVwPGr*M% zS>Fd_)OTBf26T|u_}uu+zx~T2oep)m29jji{pTTl4WU0+-5la`DAcMQV`^)Yw^X)m zb}iC|4)Pk`$OqT@GoeCVt|_y2YD3yjl}QMu#UabD54@v;Ur0O`sY*bt%e9Abo~kQ7 zWcBqRWZmv$8P_TwD4i)acJ8;lDU#T|5V8+aHUYja*FgvtS9WO%wr+1;WcbhVp^N%R z<>*XXROy z^Rl|whsiY4vsUxb3mRd7s61MMOG<=k>QQ%kL>))$g^G=--$csV-%A$a!`>~N%k2dWpf;0eB9<8b~ zt#W`2a8Ga-kPHN)&o=#;j-p$oZF}N^a{buZATwqzEeJszWLF-|o86@aJOs=RRsuqI zlR${BJes|L-#oHxzW{J`Z?HNrP%4k+pP2gyO|Lt^VqiGf4G29Lk}HqqUTng_T#Ihh zoeKm1JXaS6Lgmpe1HN}Yw3O;P;CFm(@Mi_+zYtw{G zpwX~URUC#TZh{z1Obv+S1Uw@g3coq%B!|gIlVPKZY!s<*aCKa#{GUL>kvIubqy(|^ z?f!JwZZBZv!Die;cd7=;hIubaWJbJ~Cdw3&2{Ii}l~yM|dba_Dza3q`Cz!uyF}Kj4 zDp50|z0lPp9fm-%vmjGii-g?cognZFrnJZ8VD2+ zfE}YJiv4uixnDUu!YXc$Wg7enURfX=qbFEkUxtKXRJi92@Okh#UUrP0_;K1Cqvf*S zR_G;A0c9QW{1+U+1+xp=*z6}O;AQtPt-(t>$ZO^5yZhh!>U)=tqymCud?4drFfX$k zfq7h)z$Uog`i>Uh#U12RZck8!4E{%ci4LHCJu8CE0G#nJ;N_MCKcp>BJdWlxJA3Jy$6jLKV>#$c30+g!RH~3f8mWG7|AXv z9f7kWJ=Db>UoAYhgZ#_KzccAKD@{j&B+P2=4(f+ck;ML zAO3=V7b(WQaZmr2@npUt*DKbjx48Ku3wu8_V%(HjTK$G@&i{1_kO0UZ|ALWCm;Lob zU?+Q~a}Ewus5SZcV4JVz4X^DWf56w(yx5>G%$z)S_<&xW+caxjw_1ggh0kY8pEA); zcSnm5GO!@4I+UbF;@((fZ4)VW(2;LAbKEW1PdF0|5Xpa8CLF literal 0 HcmV?d00001