1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.tikv.common.catalog;
19
20 import com.google.common.annotations.VisibleForTesting;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.ImmutableMap;
23 import java.util.*;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.function.Supplier;
26 import java.util.stream.Collectors;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.tikv.common.Snapshot;
30 import org.tikv.common.meta.TiDBInfo;
31 import org.tikv.common.meta.TiTableInfo;
32
33 public class Catalog implements AutoCloseable {
34 private final boolean showRowId;
35 private final String dbPrefix;
36 private final Logger logger = LoggerFactory.getLogger(this.getClass());
37 private final Supplier<Snapshot> snapshotProvider;
38 private CatalogCache metaCache;
39
40 public Catalog(Supplier<Snapshot> snapshotProvider, boolean showRowId, String dbPrefix) {
41 this.snapshotProvider = Objects.requireNonNull(snapshotProvider, "Snapshot Provider is null");
42 this.showRowId = showRowId;
43 this.dbPrefix = dbPrefix;
44 metaCache = new CatalogCache(new CatalogTransaction(snapshotProvider.get()), dbPrefix, false);
45 }
46
47 @Override
48 public void close() {}
49
50 private synchronized void reloadCache(boolean loadTables) {
51 Snapshot snapshot = snapshotProvider.get();
52 CatalogTransaction newTrx = new CatalogTransaction(snapshot);
53 long latestVersion = newTrx.getLatestSchemaVersion();
54 if (latestVersion > metaCache.getVersion()) {
55 metaCache = new CatalogCache(newTrx, dbPrefix, loadTables);
56 }
57 }
58
59 private void reloadCache() {
60 reloadCache(false);
61 }
62
63 public List<TiDBInfo> listDatabases() {
64 reloadCache();
65 return metaCache.listDatabases();
66 }
67
68 public List<TiTableInfo> listTables(TiDBInfo database) {
69 Objects.requireNonNull(database, "database is null");
70 reloadCache(true);
71 if (showRowId) {
72 return metaCache
73 .listTables(database)
74 .stream()
75 .map(TiTableInfo::copyTableWithRowId)
76 .collect(Collectors.toList());
77 } else {
78 return metaCache.listTables(database);
79 }
80 }
81
82 public TiDBInfo getDatabase(String dbName) {
83 Objects.requireNonNull(dbName, "dbName is null");
84 reloadCache();
85 return metaCache.getDatabase(dbName);
86 }
87
88 public TiTableInfo getTable(String dbName, String tableName) {
89 TiDBInfo database = getDatabase(dbName);
90 if (database == null) {
91 return null;
92 }
93 return getTable(database, tableName);
94 }
95
96 public TiTableInfo getTable(TiDBInfo database, String tableName) {
97 Objects.requireNonNull(database, "database is null");
98 Objects.requireNonNull(tableName, "tableName is null");
99 reloadCache(true);
100 TiTableInfo table = metaCache.getTable(database, tableName);
101 if (showRowId && table != null) {
102 return table.copyTableWithRowId();
103 } else {
104 return table;
105 }
106 }
107
108 @VisibleForTesting
109 public TiTableInfo getTable(TiDBInfo database, long tableId) {
110 Objects.requireNonNull(database, "database is null");
111 Collection<TiTableInfo> tables = listTables(database);
112 for (TiTableInfo table : tables) {
113 if (table.getId() == tableId) {
114 if (showRowId) {
115 return table.copyTableWithRowId();
116 } else {
117 return table;
118 }
119 }
120 }
121 return null;
122 }
123
124 private static class CatalogCache {
125
126 private final Map<String, TiDBInfo> dbCache;
127 private final ConcurrentHashMap<TiDBInfo, Map<String, TiTableInfo>> tableCache;
128 private final String dbPrefix;
129 private final CatalogTransaction transaction;
130 private final long currentVersion;
131
132 private CatalogCache(CatalogTransaction transaction, String dbPrefix, boolean loadTables) {
133 this.transaction = transaction;
134 this.dbPrefix = dbPrefix;
135 this.tableCache = new ConcurrentHashMap<>();
136 this.dbCache = loadDatabases(loadTables);
137 this.currentVersion = transaction.getLatestSchemaVersion();
138 }
139
140 public CatalogTransaction getTransaction() {
141 return transaction;
142 }
143
144 public long getVersion() {
145 return currentVersion;
146 }
147
148 public TiDBInfo getDatabase(String name) {
149 Objects.requireNonNull(name, "name is null");
150 return dbCache.get(name.toLowerCase());
151 }
152
153 public List<TiDBInfo> listDatabases() {
154 return ImmutableList.copyOf(dbCache.values());
155 }
156
157 public List<TiTableInfo> listTables(TiDBInfo db) {
158 Map<String, TiTableInfo> tableMap = tableCache.get(db);
159 if (tableMap == null) {
160 tableMap = loadTables(db);
161 }
162 Collection<TiTableInfo> tables = tableMap.values();
163 return tables
164 .stream()
165 .filter(tbl -> !tbl.isView() || !tbl.isSequence())
166 .collect(Collectors.toList());
167 }
168
169 public TiTableInfo getTable(TiDBInfo db, String tableName) {
170 Map<String, TiTableInfo> tableMap = tableCache.get(db);
171 if (tableMap == null) {
172 tableMap = loadTables(db);
173 }
174 TiTableInfo tbl = tableMap.get(tableName.toLowerCase());
175
176
177 if (tbl != null && (tbl.isView() || tbl.isSequence())) return null;
178 return tbl;
179 }
180
181 private Map<String, TiTableInfo> loadTables(TiDBInfo db) {
182 List<TiTableInfo> tables = transaction.getTables(db.getId());
183 ImmutableMap.Builder<String, TiTableInfo> builder = ImmutableMap.builder();
184 for (TiTableInfo table : tables) {
185 builder.put(table.getName().toLowerCase(), table);
186 }
187 Map<String, TiTableInfo> tableMap = builder.build();
188 tableCache.put(db, tableMap);
189 return tableMap;
190 }
191
192 private Map<String, TiDBInfo> loadDatabases(boolean loadTables) {
193 HashMap<String, TiDBInfo> newDBCache = new HashMap<>();
194
195 List<TiDBInfo> databases = transaction.getDatabases();
196 databases.forEach(
197 db -> {
198 TiDBInfo newDBInfo = db.rename(dbPrefix + db.getName());
199 newDBCache.put(newDBInfo.getName().toLowerCase(), newDBInfo);
200 if (loadTables) {
201 loadTables(newDBInfo);
202 }
203 });
204 return newDBCache;
205 }
206 }
207 }