{"action":"created","comment":{"url":"https://api.github.com/repos/cscenter/interestscsc/pulls/comments/43708008","id":43708008,"diff_hunk":"@@ -2,79 +2,549 @@\n \n import crawler.Post;\n import crawler.Tag;\n+import crawler.User;\n+import org.postgresql.ds.PGPoolingDataSource;\n+import org.postgresql.util.PSQLException;\n \n import java.io.File;\n import java.io.FileNotFoundException;\n-import java.sql.Connection;\n-import java.sql.DriverManager;\n-import java.sql.SQLException;\n-import java.util.Properties;\n+import java.sql.*;\n+import java.time.LocalDateTime;\n+import java.util.LinkedList;\n import java.util.Scanner;\n \n+\n /**\n  * User: allight\n  * Date: 06.10.2015 14:47\n  */\n \n public class DBConnector {\n+    private static final String DROPDATA_PASS = \"Bzw7HPtmHmVVqKvSHe7d\";\n     private static final String SCHEMA_PATH = \"db/schema.sql\";\n     private static final String SCHEMA_ENCODING = \"UTF-8\";\n-    private String host = \"localhost\"; //TODO храним здесь или оставляем метод для задания\n-    private String port = \"5432\";\n-    private String db = \"interests\";\n-    private String user = \"interests\";\n-    private String pass = \"12345\";\n-    private Connection connection;\n-\n-    public DBConnector setConnectionParams(String host, String port, String db, String user, String pass) {\n-        this.host = host;\n-        this.port = port;\n-        this.db = db;\n-        this.user = user;\n-        this.pass = pass;\n-        return this;\n+    private static final String HOST = \"185.72.144.129\";\n+    private static final int PORT = 5432; // стандартный порт в постгрес\n+    private static final String DB = \"veiloneru\";\n+    private static final String USER = \"veiloneru\";\n+    private static final String PASS = \"wasddsaw\";\n+    private static final int MAX_CONNECTIONS = 100; // 100 - внутреннее ограничение постгрес\n+    private static final int MAX_TRIES = 5; // TODO число попыток выполнения при временной (*) неудаче (>1)\n+    private PGPoolingDataSource connectionPool;\n+    private int crawler_id;\n+\n+\n+    public DBConnector(String crawler_name) throws SQLException {\n+        connectionPool = new PGPoolingDataSource();\n+        connectionPool.setServerName(HOST);\n+        connectionPool.setPortNumber(PORT);\n+        connectionPool.setDatabaseName(DB);\n+        connectionPool.setUser(USER);\n+        connectionPool.setPassword(PASS);\n+        connectionPool.setMaxConnections(MAX_CONNECTIONS);\n+        crawler_id = getCrawler_id(crawler_name);\n+\n     }\n \n-    public DBConnector connect() throws ClassNotFoundException, SQLException {\n-        Class.forName(\"org.postgresql.Driver\");\n-        String url = \"jdbc:postgresql://\" + host + \":\" + port + \"/\" + db;\n-        Properties props = new Properties();\n-        props.setProperty(\"user\", user);\n-        props.setProperty(\"password\", pass);\n-        connection = DriverManager.getConnection(url, props);\n-        return this;\n+\n+    private int getCrawler_id(String crawler_name) throws SQLException {\n+        String insertCrawlerString =\n+                \"INSERT INTO Crawler VALUES (DEFAULT , ?);\";\n+        String selectCrawlerString = \"(SELECT id FROM Crawler WHERE name = ?);\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertCrawler = con.prepareStatement(insertCrawlerString);\n+                PreparedStatement selectCrawler = con.prepareStatement(selectCrawlerString)\n+        ) {\n+            insertCrawler.setString(1, crawler_name);\n+            tryUpdateTransaction(insertCrawler, crawler_name, \"Crawler\");\n+            selectCrawler.setString(1, crawler_name);\n+            ResultSet rs = tryQueryTransaction(selectCrawler, \"Crawler\");\n+            if (rs == null || !rs.next())\n+                throw new IllegalStateException(\"If you see this, our code needs a fix\");\n+            return rs.getInt(1);\n+        }\n     }\n \n-    public Connection getConnection() throws SQLException, ClassNotFoundException {\n-        if (connection == null) connect();\n-        return connection;\n+\n+    /**\n+     * Выдает соединение для использования в качестве контекстного объекта.\n+     * ! Необходимо самостоятельно вернуть соединение в пул методом\n+     * myDBConnector.closeConnection(conn) или conn.close(). Лучше всего\n+     * использовать try-with-resources (см., например, метод dropInitDatabase).\n+     */\n+    public Connection getConnection() throws SQLException {\n+        return connectionPool.getConnection();\n     }\n \n-    public DBConnector recreateDatabase() throws SQLException, FileNotFoundException {\n-        String schemaSQL = new Scanner(new File(SCHEMA_PATH), SCHEMA_ENCODING).useDelimiter(\"\\\\Z\").next();\n-        connection.createStatement().execute(schemaSQL);\n-        return this;\n+\n+    /**\n+     * Возвращает соединение в очередь.\n+     *\n+     * @deprecated Лучше использовать соедиление в качестве ресурса в\n+     * try-with-resources (см., например, метод dropInitDatabase).\n+     */\n+    public void closeConnection(Connection connectionToClose) throws SQLException {\n+        //noinspection EmptyTryBlock\n+        try (Connection autoclose = connectionToClose) {\n+        }\n     }\n \n-    public DBConnector insertTag(Tag tag) throws SQLException { //TODO нужно как-то возвращать успешность внесенных изменений\n-        connection.createStatement().execute(\"INSERT INTO Tag VALUES \" +\n-                \"(DEFAULT, '\" + tag.getName() + \"', NULL, NULL );\");\n+\n+    /**\n+     * Пересоздает базу из схемы, указанной в SCHEMA_PATH с кодировкой\n+     * SCHEMA_ENCODING\n+     */\n+    public DBConnector dropInitDatabase(String newCrawler_name, String pass) throws FileNotFoundException, SQLException {\n+        if (!pass.equalsIgnoreCase(DROPDATA_PASS))\n+            throw new IllegalArgumentException(\"Maybe you shouldn't drop db?\");\n+        String schemaSQL = new Scanner(new File(SCHEMA_PATH), SCHEMA_ENCODING)\n+                .useDelimiter(\"\\\\Z\").next();\n+        try (Connection conn = getConnection()) {\n+            conn.createStatement().execute(schemaSQL);\n+        }\n+        crawler_id = getCrawler_id(newCrawler_name);\n         return this;\n     }\n \n-    public DBConnector insertPost(Post post) throws SQLException {\n-        connection.createStatement().execute( //Не очень красивый способ. найти параметризованный\n-                \"INSERT INTO Post VALUES \" +\n-                        \"(DEFAULT, \" +\n-                        post.getUrl() + \", \" +\n-                        \"(SELECT id FROM UserLJ WHERE nick = '\" + post.getAuthor() + \"'), \" +\n-                        \"'\" + post.getDate() + \"', \" +\n-                        \"'\" + post.getTitle() + \"', \" +\n-                        \"'\" + post.getText() + \"', \" +\n-                        \"NULL, \" +\n-                        post.getCountComment() +\n-                        \");\"\n-        );\n-        return this;\n+\n+    /**\n+     * Добавляет в БД регион.\n+     *\n+     * @return кол-во добавленных записей\n+     */\n+    public int insertRegion(String region) throws SQLException {\n+        int rowsAffected = 0;\n+        String insertRegionString =\n+                \"INSERT INTO Region VALUES (DEFAULT, ?);\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertRegion = con.prepareStatement(insertRegionString)\n+        ) {\n+            insertRegion.setString(1, region);\n+            rowsAffected += tryUpdateTransaction(insertRegion, region, \"Region\");\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    public LinkedList<String> getRegions() throws SQLException {\n+        LinkedList<String> result = new LinkedList<>();\n+        String selectRegionsString = \"SELECT name FROM Region;\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement selectRegions = con.prepareStatement(selectRegionsString);\n+        ) {\n+            ResultSet rs = tryQueryTransaction(selectRegions, \"Region\");\n+            if (rs != null)\n+                while (rs.next())\n+                    result.add(rs.getString(1));\n+        }\n+        return result;\n+    }\n+\n+\n+    /**\n+     * Поочередно добавляет в БД пользователей из любого итерабельного контейнера.\n+     * Информация о регионах добавляемых пользователей уже должна быть в базе.\n+     *\n+     * @return кол-во добавленных записей\n+     */\n+    public int insertRawUsers(Iterable<String> users) throws SQLException {\n+        int rowsAffected = 0;\n+        String insertUserString =\n+                \"INSERT INTO RawUserLJ VALUES (?, NULL);\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertUser = con.prepareStatement(insertUserString)\n+        ) {\n+            for (String user : users) {\n+                insertUser.setString(1, user);\n+                rowsAffected += tryUpdateTransaction(insertUser, user, \"RawUserLJ\");\n+            }\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    /**\n+     * Резервирует в требуемое количество необработанных пользователей под указанный краулер.\n+     * Название краулера уже должно быть в таблице Crawler.\n+     *\n+     * @return кол-во добавленных записей\n+     */\n+    public int reserveRawUserForCrawler(int rowNum) throws SQLException {         //TODO сделать crawler_name полем\n+        if (rowNum <= 0) throw new IllegalArgumentException(\"Argument rowNum must be greater than 0.\");\n+        int rowsAffected = 0;\n+        String reserveUserNicksString =\n+                \"UPDATE RawUserLJ r SET crawler_id = ? \" +\n+                        \"FROM ( \" +\n+                        \"       SELECT nick FROM RawUserLJ r \" +\n+                        \"       WHERE r.crawler_id IS NULL AND r.user_id IS NULL \" +\n+                        \"       LIMIT ? FOR UPDATE \" +\n+                        \"     ) free \" +\n+                        \"WHERE r.nick = free.nick;\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement reserveUserNicks = con.prepareStatement(reserveUserNicksString)\n+        ) {\n+            reserveUserNicks.setInt(1, crawler_id);\n+            reserveUserNicks.setInt(2, rowNum);\n+            rowsAffected += tryUpdateTransaction(reserveUserNicks, \"crawler_id = \" + crawler_id, \"RawUserLJ\");\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    public LinkedList<String> getReservedRawUsers() throws SQLException {\n+        LinkedList<String> result = new LinkedList<>();\n+        String selectReservedString = \"SELECT nick FROM RawUserLJ \" +\n+                \"WHERE user_id IS NULL AND crawler_id = ?;\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement selectReserved = con.prepareStatement(selectReservedString);\n+        ) {\n+            selectReserved.setInt(1, crawler_id);\n+            ResultSet rs = tryQueryTransaction(selectReserved, \"RawUserLJ\");\n+            if (rs != null)\n+                while (rs.next())\n+                    result.add(rs.getString(1));\n+        }\n+        return result;\n+    }\n+\n+\n+    public LinkedList<String> getUnfinishedRawUsers() throws SQLException {\n+        LinkedList<String> result = new LinkedList<>();\n+        String selectUnfinishedString = \"SELECT r.nick \" +\n+                \"FROM RawUserLJ r JOIN UserLJ u ON r.user_id = u.id \" +\n+                \"WHERE r.crawler_id = ? AND u.fetched IS NULL;\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement selectUnfinished = con.prepareStatement(selectUnfinishedString);\n+        ) {\n+            selectUnfinished.setInt(1, crawler_id);\n+            ResultSet rs = tryQueryTransaction(selectUnfinished, \"RawUserLJ JOIN UserLJ\");\n+            if (rs != null)\n+                while (rs.next())\n+                    result.add(rs.getString(1));\n+        }\n+        return result;\n+    }\n+\n+\n+    /**\n+     * Поочередно добавляет в БД пользователей из любого итерабельного контейнера.\n+     * Информация о регионах добавляемых пользователей уже должна быть в базе, их\n+     * имена должны присутствовать в RawUserLJ.\n+     *\n+     * @return кол-во добавленных записей\n+     */\n+    public int insertUser(User user) throws SQLException {\n+        int rowsAffected = 0;\n+        String insertUserString =\n+                \"INSERT INTO UserLJ VALUES (DEFAULT, ?, (SELECT id FROM Region WHERE name = COALESCE(?,'')), ?, ?, NULL, ?, ?);\";\n+        String updateRawUserString =\n+                \"UPDATE RawUserLJ SET user_id = (SELECT id FROM UserLJ WHERE nick = ?) WHERE nick = ?;\";\n+\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertUser = con.prepareStatement(insertUserString);\n+                PreparedStatement updateRawUser = con.prepareStatement(updateRawUserString)\n+        ) {\n+            insertUser.setString(1, user.getNick());\n+            insertUser.setString(2, user.getRegion());\n+            insertUser.setTimestamp(3, user.getDateCreated());\n+            insertUser.setTimestamp(4, user.getDateUpdated());\n+            insertUser.setDate(5, user.getBirthday());\n+            insertUser.setString(6, user.getInterests());\n+\n+            rowsAffected += tryUpdateTransaction(insertUser, user.getNick(), \"UserLJ\");\n+\n+            updateRawUser.setString(1, user.getNick());\n+            updateRawUser.setString(2, user.getNick());\n+\n+            rowsAffected += tryUpdateTransaction(updateRawUser, user.getNick(), \"RawUserLJ\");\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    public int updateUserFetched(String user_name) throws SQLException {\n+        int rowsAffected = 0;\n+        String updateFetchedString =\n+                \"UPDATE UserLJ SET fetched = ? WHERE nick = ?;\";\n+\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement updateFetched = con.prepareStatement(updateFetchedString)\n+        ) {\n+            updateFetched.setTimestamp(1, Timestamp.valueOf(LocalDateTime.now()));\n+            updateFetched.setString(2, user_name);\n+\n+            rowsAffected += tryUpdateTransaction(updateFetched, user_name, \"UserLJ\");\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    public LinkedList<User> getUsers() throws SQLException { //TODO сейчас не тащит fetched, т.к. его нет в User\n+        LinkedList<User> result = new LinkedList<>();\n+        String selectUsersString = \"SELECT u.nick, r.name region, \" +\n+                \"u.created, u.update, u.birthday, u.interests \" +\n+                \"FROM UserLJ u JOIN Region r ON u.region_id = r.id;\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement selectUsers = con.prepareStatement(selectUsersString);\n+        ) {\n+            ResultSet rs = tryQueryTransaction(selectUsers, \"UserLJ\");\n+            if (rs != null)\n+                while (rs.next())\n+                    result.add(new User(\n+                            rs.getString(1), rs.getString(2),\n+                            rs.getTimestamp(3), rs.getTimestamp(4),\n+                            rs.getDate(5), rs.getString(6)\n+                    ));\n+        }\n+        return result;\n+    }\n+\n+\n+    /**\n+     * Поочередно добавляет в БД тэги из любого итерабельного контейнера.\n+     * Информация о пользователе добавляемых тэгов уже должна быть в базе,\n+     * в таблице UserLJ поле nick.\n+     *\n+     * @return кол-во добавленных записей\n+     */\n+    public int insertTags(Iterable<Tag> tags, String userLJ_nick) throws SQLException {\n+        int rowsAffected = 0;\n+        String insertTagString = \"INSERT INTO Tag (id, text) VALUES (DEFAULT, ?);\";\n+        String insertTagToUserLJString = \"INSERT INTO TagToUserLJ VALUES (\" +\n+                \"(SELECT id FROM Tag WHERE text = ?), \" +\n+                \"(SELECT id FROM UserLJ WHERE nick = ?), \" +\n+                \"?);\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertTag = con.prepareStatement(insertTagString);\n+                PreparedStatement insertTagToUserLJ = con.prepareStatement(insertTagToUserLJString)\n+        ) {\n+            for (Tag tag : tags) {\n+                insertTag.setString(1, tag.getName());\n+                rowsAffected += tryUpdateTransaction(insertTag, tag.getName(), \"Tag\");\n+\n+                insertTagToUserLJ.setString(1, tag.getName());\n+                insertTagToUserLJ.setString(2, userLJ_nick);\n+                if (tag.getUses() == null)\n+                    insertTagToUserLJ.setNull(3, Types.INTEGER);\n+                else insertTagToUserLJ.setInt(3, tag.getUses());\n+                rowsAffected += tryUpdateTransaction(insertTagToUserLJ, tag.getName() + \"<->\" + userLJ_nick, \"TagToUserLJ\");\n+            }\n+        }\n+        return rowsAffected;\n+    }\n+\n+    /**\n+     * Поочередно добавляет в БД посты из любого итерабельного контейнера.\n+     * Информация об авторах добавляемых постов уже должна быть в базе.\n+     *\n+     * @return кол-во добавленных записей\n+     */\n+    public int insertPosts(Iterable<Post> posts) throws SQLException {\n+        int rowsAffected = 0;\n+        String insertPostString =\n+                \"INSERT INTO Post VALUES (DEFAULT, ?, (SELECT id FROM UserLJ WHERE nick = ?), ?, ?, ?, NULL, ?);\";\n+        String insertTagToPostString = \"INSERT INTO TagToPost VALUES (\" +\n+                \"(SELECT id FROM Tag WHERE text = ?), \" +\n+                \"(SELECT id FROM Post WHERE user_id = (SELECT id FROM UserLJ WHERE nick = ?) AND url = ?));\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertPost = con.prepareStatement(insertPostString);\n+                PreparedStatement insertTagToPost = con.prepareStatement(insertTagToPostString)\n+        ) {\n+            for (Post post : posts) {\n+                insertPost.setInt(1, post.getUrl());        //never null\n+                insertPost.setString(2, post.getAuthor());\n+                insertPost.setTimestamp(3, post.getDate());\n+                insertPost.setString(4, post.getTitle());\n+                insertPost.setString(5, post.getText());\n+                if (post.getCountComment() == null)\n+                    insertPost.setNull(6, Types.INTEGER);\n+                else insertPost.setInt(6, post.getCountComment());\n+\n+                rowsAffected += tryUpdateTransaction(insertPost, post.getAuthor() + \">\" + post.getUrl(), \"Post\");\n+\n+                for (String tag : post.getTags()) {\n+                    insertTagToPost.setString(1, tag);\n+                    insertTagToPost.setString(2, post.getAuthor());\n+                    insertTagToPost.setInt(3, post.getUrl());      //never null\n+                    rowsAffected += tryUpdateTransaction(insertTagToPost, post.getAuthor() + \">\" + post.getUrl() + \"<->\" + tag, \"TagTOPost\");\n+                }\n+\n+            }\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    /**\n+     * Поочередно добавляет в БД n-граммы из любого итерабельного контейнера.\n+     * Информация о посте добавляемых n-грамм уже должна быть в базе,\n+     * в таблице UserLJ поле nick.\n+     *\n+     * @param ngrams      - итерабельный контейнер с n-граммами в строках.\n+     * @param userLJ_nick - автор поста\n+     * @param post_url    - url поста\n+     * @param ngramType   - тип добавляемых n-грамм. [1..3]\n+     *                    1 - unigram, 2 - digram, 3 - trigram.\n+     * @return кол-во добавленных записей\n+     */\n+    public int insertNgrams(Iterable<String> ngrams, String userLJ_nick, int post_url, int ngramType) throws SQLException {\n+        String ngramTableName;\n+        switch (ngramType) {\n+            case 1:\n+                ngramTableName = \"unigram\";\n+                break;\n+            case 2:\n+                ngramTableName = \"digram\";\n+                break;\n+            case 3:\n+                ngramTableName = \"trigram\";\n+                break;\n+            default:\n+                throw new IllegalArgumentException(\"Argument ngramType must be in range [1..3].\");\n+        }\n+        int rowsAffected = 0;\n+        String insertNgramString = \"INSERT INTO \" + ngramTableName + \" VALUES (DEFAULT, ?);\";\n+        String insertNgramToPostString = \"INSERT INTO \" + ngramTableName + \"ToPost VALUES (\" +\n+                \"(SELECT id FROM \" + ngramTableName + \" WHERE text = ?), \" +\n+                \"(SELECT id FROM Post WHERE user_id = (SELECT id FROM UserLJ WHERE nick = ?) AND url = ?));\";\n+        try (\n+                Connection con = getConnection();\n+                PreparedStatement insertNgram = con.prepareStatement(insertNgramString);\n+                PreparedStatement insertNgramToPost = con.prepareStatement(insertNgramToPostString)\n+        ) {\n+            for (String ngram : ngrams) {\n+                insertNgram.setString(1, ngram);\n+                rowsAffected += tryUpdateTransaction(insertNgram, ngram, ngramTableName);\n+\n+                insertNgramToPost.setString(1, ngram);\n+                insertNgramToPost.setString(2, userLJ_nick);\n+                insertNgramToPost.setInt(3, post_url);        //never null\n+                rowsAffected += tryUpdateTransaction(insertNgramToPost, userLJ_nick + \">\" + post_url + \"<->\" + ngram, ngramTableName + \"ToPost\");\n+            }\n+        }\n+        return rowsAffected;\n+    }\n+\n+\n+    /**\n+     * Пытается выполнить запрос к БД, обрабатывая некоторые исключения.\n+     * При возникновении некоторых исключений, повторяет запрос до MAX_TRIES-1 раз.\n+     *\n+     * @return объект класса <code>ResultSet</code>, содержащий результат запроса;\n+     * надо проверять на <code>null</code>      //TODO хочется никогда не возвращать null\n+     */\n+    private ResultSet tryQueryTransaction(PreparedStatement statement, String tableName) throws SQLException {   //SQLException пробрасываем\n+        boolean retryTransaction = true;\n+        int tries = MAX_TRIES;\n+        ResultSet resultSet = null;\n+        while (retryTransaction && tries > 0)\n+            try {\n+                retryTransaction = false;\n+                --tries;\n+                resultSet = statement.executeQuery();\n+            } catch (PSQLException pse) {\n+                final String ss = pse.getSQLState();\n+                if (ss.equals(\"23505\")) { // unique_violation\n+                    System.err.println(\"PSQLException: unique_violation on query <\" +\n+                            statement + \">. Continue.\"); //TODO statement печатается норм\n+                    break;\n+                } else if (ss.equals(\"40001\") || ss.equals(\"40P01\")) { // serialization_failure, deadlock_detected\n+                    retryTransaction = true; //TODO переписать как отдельные методы\n+                    System.err.println(\"PSQLException: serialization_failure or deadlock_detected. \" +\n+                            \"Retrying transaction.\");\n+                } else if (ss.startsWith(\"08\") || ss.startsWith(\"53\")) { //connection_exception, insufficient_resources\n+                    System.err.println(\"PSQLException: suspected bad connection. Closing query transaction to \" +\n+                            \"table \" + tableName + \". \\n If you see this exception than we need a code fix\");\n+                    //TODO как-то по другому обрабатывать?\n+                    throw pse;\n+                } else {\n+                    System.err.println(ss);\n+                    throw pse;\n+                }\n+            }\n+        return resultSet;\n+    }\n+\n+\n+    private int tryUpdateTransaction(PreparedStatement toExecute, String currEntry, String tableName) throws SQLException {   //SQLException пробрасываем\n+        boolean retryTransaction = true;\n+        int tries = MAX_TRIES;\n+        int affected = 0;\n+        while (retryTransaction && tries > 0)\n+            try {\n+                retryTransaction = false;\n+                --tries;\n+                affected += toExecute.executeUpdate();\n+            } catch (PSQLException pse) {\n+                final String ss = pse.getSQLState();\n+                if (ss.equals(\"23505\")) { // unique_violation\n+                    System.err.println(\"PSQLException: unique_violation. We already have entry \\\"\" +\n+                            currEntry + \"\\\" in table \\\"\" + tableName + \"\\\". Continue.\"); //TODO нормальный лог\n+                    break;\n+                } else if (ss.equals(\"40001\") || ss.equals(\"40P01\")) { // serialization_failure, deadlock_detected\n+                    retryTransaction = true;\n+                    System.err.println(\"PSQLException: serialization_failure or deadlock_detected. \" +\n+                            \"Retrying transaction.\");\n+                } else if (ss.startsWith(\"08\") || ss.startsWith(\"53\")) { //connection_exception, insufficient_resources\n+                    System.err.println(\"PSQLException: suspected bad connection. Closing transaction \" +\n+                            \"with first unadded entry: \" + currEntry + \". \\n If you see this exception than we need a code fix\");\n+                    //TODO как-то по другому обрабатывать?\n+                    throw pse;","path":"db/src/main/java/db/DBConnector.java","position":553,"original_position":553,"commit_id":"540d1771d9706f40a507eb15b15a1dd416430a39","original_commit_id":"540d1771d9706f40a507eb15b15a1dd416430a39","user":{"login":"Allight7","id":4986998,"avatar_url":"https://avatars.githubusercontent.com/u/4986998?v=3","gravatar_id":"","url":"https://api.github.com/users/Allight7","html_url":"https://github.com/Allight7","followers_url":"https://api.github.com/users/Allight7/followers","following_url":"https://api.github.com/users/Allight7/following{/other_user}","gists_url":"https://api.github.com/users/Allight7/gists{/gist_id}","starred_url":"https://api.github.com/users/Allight7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Allight7/subscriptions","organizations_url":"https://api.github.com/users/Allight7/orgs","repos_url":"https://api.github.com/users/Allight7/repos","events_url":"https://api.github.com/users/Allight7/events{/privacy}","received_events_url":"https://api.github.com/users/Allight7/received_events","type":"User","site_admin":false},"body":"с любым. мысль в том, что обработать можно только то исключение, кот точно знаешь как. по остальным падать нормально, у нас же не продакшн пока. :)","created_at":"2015-11-03T01:33:32Z","updated_at":"2015-11-03T01:33:32Z","html_url":"https://github.com/cscenter/interestscsc/pull/7#discussion_r43708008","pull_request_url":"https://api.github.com/repos/cscenter/interestscsc/pulls/7","_links":{"self":{"href":"https://api.github.com/repos/cscenter/interestscsc/pulls/comments/43708008"},"html":{"href":"https://github.com/cscenter/interestscsc/pull/7#discussion_r43708008"},"pull_request":{"href":"https://api.github.com/repos/cscenter/interestscsc/pulls/7"}}},"pull_request":{"url":"https://api.github.com/repos/cscenter/interestscsc/pulls/7","id":49020187,"html_url":"https://github.com/cscenter/interestscsc/pull/7","diff_url":"https://github.com/cscenter/interestscsc/pull/7.diff","patch_url":"https://github.com/cscenter/interestscsc/pull/7.patch","issue_url":"https://api.github.com/repos/cscenter/interestscsc/issues/7","number":7,"state":"open","locked":false,"title":"DB Connector interface added","user":{"login":"Allight7","id":4986998,"avatar_url":"https://avatars.githubusercontent.com/u/4986998?v=3","gravatar_id":"","url":"https://api.github.com/users/Allight7","html_url":"https://github.com/Allight7","followers_url":"https://api.github.com/users/Allight7/followers","following_url":"https://api.github.com/users/Allight7/following{/other_user}","gists_url":"https://api.github.com/users/Allight7/gists{/gist_id}","starred_url":"https://api.github.com/users/Allight7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Allight7/subscriptions","organizations_url":"https://api.github.com/users/Allight7/orgs","repos_url":"https://api.github.com/users/Allight7/repos","events_url":"https://api.github.com/users/Allight7/events{/privacy}","received_events_url":"https://api.github.com/users/Allight7/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-10-28T15:16:54Z","updated_at":"2015-11-03T01:33:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"743609e6794f0d95a14022bf8ff026a2bde39827","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cscenter/interestscsc/pulls/7/commits","review_comments_url":"https://api.github.com/repos/cscenter/interestscsc/pulls/7/comments","review_comment_url":"https://api.github.com/repos/cscenter/interestscsc/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cscenter/interestscsc/issues/7/comments","statuses_url":"https://api.github.com/repos/cscenter/interestscsc/statuses/540d1771d9706f40a507eb15b15a1dd416430a39","head":{"label":"cscenter:DBInterface","ref":"DBInterface","sha":"540d1771d9706f40a507eb15b15a1dd416430a39","user":{"login":"cscenter","id":5622041,"avatar_url":"https://avatars.githubusercontent.com/u/5622041?v=3","gravatar_id":"","url":"https://api.github.com/users/cscenter","html_url":"https://github.com/cscenter","followers_url":"https://api.github.com/users/cscenter/followers","following_url":"https://api.github.com/users/cscenter/following{/other_user}","gists_url":"https://api.github.com/users/cscenter/gists{/gist_id}","starred_url":"https://api.github.com/users/cscenter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cscenter/subscriptions","organizations_url":"https://api.github.com/users/cscenter/orgs","repos_url":"https://api.github.com/users/cscenter/repos","events_url":"https://api.github.com/users/cscenter/events{/privacy}","received_events_url":"https://api.github.com/users/cscenter/received_events","type":"Organization","site_admin":false},"repo":{"id":42926815,"name":"interestscsc","full_name":"cscenter/interestscsc","owner":{"login":"cscenter","id":5622041,"avatar_url":"https://avatars.githubusercontent.com/u/5622041?v=3","gravatar_id":"","url":"https://api.github.com/users/cscenter","html_url":"https://github.com/cscenter","followers_url":"https://api.github.com/users/cscenter/followers","following_url":"https://api.github.com/users/cscenter/following{/other_user}","gists_url":"https://api.github.com/users/cscenter/gists{/gist_id}","starred_url":"https://api.github.com/users/cscenter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cscenter/subscriptions","organizations_url":"https://api.github.com/users/cscenter/orgs","repos_url":"https://api.github.com/users/cscenter/repos","events_url":"https://api.github.com/users/cscenter/events{/privacy}","received_events_url":"https://api.github.com/users/cscenter/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cscenter/interestscsc","description":"","fork":false,"url":"https://api.github.com/repos/cscenter/interestscsc","forks_url":"https://api.github.com/repos/cscenter/interestscsc/forks","keys_url":"https://api.github.com/repos/cscenter/interestscsc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cscenter/interestscsc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cscenter/interestscsc/teams","hooks_url":"https://api.github.com/repos/cscenter/interestscsc/hooks","issue_events_url":"https://api.github.com/repos/cscenter/interestscsc/issues/events{/number}","events_url":"https://api.github.com/repos/cscenter/interestscsc/events","assignees_url":"https://api.github.com/repos/cscenter/interestscsc/assignees{/user}","branches_url":"https://api.github.com/repos/cscenter/interestscsc/branches{/branch}","tags_url":"https://api.github.com/repos/cscenter/interestscsc/tags","blobs_url":"https://api.github.com/repos/cscenter/interestscsc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cscenter/interestscsc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cscenter/interestscsc/git/refs{/sha}","trees_url":"https://api.github.com/repos/cscenter/interestscsc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cscenter/interestscsc/statuses/{sha}","languages_url":"https://api.github.com/repos/cscenter/interestscsc/languages","stargazers_url":"https://api.github.com/repos/cscenter/interestscsc/stargazers","contributors_url":"https://api.github.com/repos/cscenter/interestscsc/contributors","subscribers_url":"https://api.github.com/repos/cscenter/interestscsc/subscribers","subscription_url":"https://api.github.com/repos/cscenter/interestscsc/subscription","commits_url":"https://api.github.com/repos/cscenter/interestscsc/commits{/sha}","git_commits_url":"https://api.github.com/repos/cscenter/interestscsc/git/commits{/sha}","comments_url":"https://api.github.com/repos/cscenter/interestscsc/comments{/number}","issue_comment_url":"https://api.github.com/repos/cscenter/interestscsc/issues/comments{/number}","contents_url":"https://api.github.com/repos/cscenter/interestscsc/contents/{+path}","compare_url":"https://api.github.com/repos/cscenter/interestscsc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cscenter/interestscsc/merges","archive_url":"https://api.github.com/repos/cscenter/interestscsc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cscenter/interestscsc/downloads","issues_url":"https://api.github.com/repos/cscenter/interestscsc/issues{/number}","pulls_url":"https://api.github.com/repos/cscenter/interestscsc/pulls{/number}","milestones_url":"https://api.github.com/repos/cscenter/interestscsc/milestones{/number}","notifications_url":"https://api.github.com/repos/cscenter/interestscsc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cscenter/interestscsc/labels{/name}","releases_url":"https://api.github.com/repos/cscenter/interestscsc/releases{/id}","created_at":"2015-09-22T10:18:16Z","updated_at":"2015-09-30T23:27:59Z","pushed_at":"2015-11-02T17:17:44Z","git_url":"git://github.com/cscenter/interestscsc.git","ssh_url":"git@github.com:cscenter/interestscsc.git","clone_url":"https://github.com/cscenter/interestscsc.git","svn_url":"https://github.com/cscenter/interestscsc","homepage":null,"size":892,"stargazers_count":1,"watchers_count":1,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":3,"forks":0,"open_issues":3,"watchers":1,"default_branch":"master"}},"base":{"label":"cscenter:crawler","ref":"crawler","sha":"2a8885c8029709500988d2b7ed987634872bece9","user":{"login":"cscenter","id":5622041,"avatar_url":"https://avatars.githubusercontent.com/u/5622041?v=3","gravatar_id":"","url":"https://api.github.com/users/cscenter","html_url":"https://github.com/cscenter","followers_url":"https://api.github.com/users/cscenter/followers","following_url":"https://api.github.com/users/cscenter/following{/other_user}","gists_url":"https://api.github.com/users/cscenter/gists{/gist_id}","starred_url":"https://api.github.com/users/cscenter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cscenter/subscriptions","organizations_url":"https://api.github.com/users/cscenter/orgs","repos_url":"https://api.github.com/users/cscenter/repos","events_url":"https://api.github.com/users/cscenter/events{/privacy}","received_events_url":"https://api.github.com/users/cscenter/received_events","type":"Organization","site_admin":false},"repo":{"id":42926815,"name":"interestscsc","full_name":"cscenter/interestscsc","owner":{"login":"cscenter","id":5622041,"avatar_url":"https://avatars.githubusercontent.com/u/5622041?v=3","gravatar_id":"","url":"https://api.github.com/users/cscenter","html_url":"https://github.com/cscenter","followers_url":"https://api.github.com/users/cscenter/followers","following_url":"https://api.github.com/users/cscenter/following{/other_user}","gists_url":"https://api.github.com/users/cscenter/gists{/gist_id}","starred_url":"https://api.github.com/users/cscenter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cscenter/subscriptions","organizations_url":"https://api.github.com/users/cscenter/orgs","repos_url":"https://api.github.com/users/cscenter/repos","events_url":"https://api.github.com/users/cscenter/events{/privacy}","received_events_url":"https://api.github.com/users/cscenter/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cscenter/interestscsc","description":"","fork":false,"url":"https://api.github.com/repos/cscenter/interestscsc","forks_url":"https://api.github.com/repos/cscenter/interestscsc/forks","keys_url":"https://api.github.com/repos/cscenter/interestscsc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cscenter/interestscsc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cscenter/interestscsc/teams","hooks_url":"https://api.github.com/repos/cscenter/interestscsc/hooks","issue_events_url":"https://api.github.com/repos/cscenter/interestscsc/issues/events{/number}","events_url":"https://api.github.com/repos/cscenter/interestscsc/events","assignees_url":"https://api.github.com/repos/cscenter/interestscsc/assignees{/user}","branches_url":"https://api.github.com/repos/cscenter/interestscsc/branches{/branch}","tags_url":"https://api.github.com/repos/cscenter/interestscsc/tags","blobs_url":"https://api.github.com/repos/cscenter/interestscsc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cscenter/interestscsc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cscenter/interestscsc/git/refs{/sha}","trees_url":"https://api.github.com/repos/cscenter/interestscsc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cscenter/interestscsc/statuses/{sha}","languages_url":"https://api.github.com/repos/cscenter/interestscsc/languages","stargazers_url":"https://api.github.com/repos/cscenter/interestscsc/stargazers","contributors_url":"https://api.github.com/repos/cscenter/interestscsc/contributors","subscribers_url":"https://api.github.com/repos/cscenter/interestscsc/subscribers","subscription_url":"https://api.github.com/repos/cscenter/interestscsc/subscription","commits_url":"https://api.github.com/repos/cscenter/interestscsc/commits{/sha}","git_commits_url":"https://api.github.com/repos/cscenter/interestscsc/git/commits{/sha}","comments_url":"https://api.github.com/repos/cscenter/interestscsc/comments{/number}","issue_comment_url":"https://api.github.com/repos/cscenter/interestscsc/issues/comments{/number}","contents_url":"https://api.github.com/repos/cscenter/interestscsc/contents/{+path}","compare_url":"https://api.github.com/repos/cscenter/interestscsc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cscenter/interestscsc/merges","archive_url":"https://api.github.com/repos/cscenter/interestscsc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cscenter/interestscsc/downloads","issues_url":"https://api.github.com/repos/cscenter/interestscsc/issues{/number}","pulls_url":"https://api.github.com/repos/cscenter/interestscsc/pulls{/number}","milestones_url":"https://api.github.com/repos/cscenter/interestscsc/milestones{/number}","notifications_url":"https://api.github.com/repos/cscenter/interestscsc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cscenter/interestscsc/labels{/name}","releases_url":"https://api.github.com/repos/cscenter/interestscsc/releases{/id}","created_at":"2015-09-22T10:18:16Z","updated_at":"2015-09-30T23:27:59Z","pushed_at":"2015-11-02T17:17:44Z","git_url":"git://github.com/cscenter/interestscsc.git","ssh_url":"git@github.com:cscenter/interestscsc.git","clone_url":"https://github.com/cscenter/interestscsc.git","svn_url":"https://github.com/cscenter/interestscsc","homepage":null,"size":892,"stargazers_count":1,"watchers_count":1,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":3,"forks":0,"open_issues":3,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cscenter/interestscsc/pulls/7"},"html":{"href":"https://github.com/cscenter/interestscsc/pull/7"},"issue":{"href":"https://api.github.com/repos/cscenter/interestscsc/issues/7"},"comments":{"href":"https://api.github.com/repos/cscenter/interestscsc/issues/7/comments"},"review_comments":{"href":"https://api.github.com/repos/cscenter/interestscsc/pulls/7/comments"},"review_comment":{"href":"https://api.github.com/repos/cscenter/interestscsc/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cscenter/interestscsc/pulls/7/commits"},"statuses":{"href":"https://api.github.com/repos/cscenter/interestscsc/statuses/540d1771d9706f40a507eb15b15a1dd416430a39"}}}}