1 /***********************************************************************
2  * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * http://www.opensource.org/licenses/apache2.0.php.
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.index.metadata
10 
11 import com.github.benmanes.caffeine.cache.{Cache, CacheLoader, Caffeine, LoadingCache}
12 import com.typesafe.scalalogging.LazyLogging
13 import org.locationtech.geomesa.utils.collection.CloseableIterator
14 import org.locationtech.geomesa.utils.conf.GeoMesaSystemProperties.SystemProperty
15 import org.locationtech.geomesa.utils.io.WithClose
16 import org.locationtech.geomesa.utils.text.DateParsing
17 
18 import java.time.format.DateTimeFormatterBuilder
19 import java.time.temporal.ChronoField
20 import java.time.{Instant, ZoneOffset}
21 import java.util.Locale
22 import java.util.concurrent.TimeUnit
23 import java.util.concurrent.atomic.AtomicBoolean
24 
25 /**
26   * Metadata persisted in a database table. The underlying table will be lazily created when required.
27   * Metadata values are cached with a configurable timeout to save repeated database reads.
28   *
29   * @tparam T type param
30   */
31 trait TableBasedMetadata[T] extends GeoMesaMetadata[T] with LazyLogging {
32 
33   import scala.collection.JavaConverters._
34 
35   /**
36     * Serializer
37     *
38     * @return
39     */
40   def serializer: MetadataSerializer[T]
41 
42   /**
43     * Checks if the underlying table exists
44     *
45     * @return
46     */
47   protected def checkIfTableExists: Boolean
48 
49   /**
50     * Creates the underlying table
51     */
52   protected def createTable(): Unit
53 
54   /**
55     * Create an instance to use for backup
56     *
57     * @param timestamp formatted timestamp for the current time
58     * @return
59     */
60   protected def createEmptyBackup(timestamp: String): TableBasedMetadata[T]
61 
62   /**
63     * Writes key/value pairs
64     *
65     * @param rows keys/values
66     */
67   protected def write(typeName: String, rows: Seq[(String, Array[Byte])]): Unit
68 
69   /**
70     * Deletes multiple rows
71     *
72     * @param typeName simple feature type name
73     * @param keys keys
74     */
75   protected def delete(typeName: String, keys: Seq[String])
76 
77   /**
78     * Reads a value from the underlying table
79     *
80     * @param typeName simple feature type name
81     * @param key key
82     * @return value, if it exists
83     */
84   protected def scanValue(typeName: String, key: String): Option[Array[Byte]]
85 
86   /**
87     * Reads row keys from the underlying table
88     *
89     * @param typeName simple feature type name
90     * @param prefix scan prefix, or empty string for all values
91     * @return matching tuples of (key, value)
92     */
93   protected def scanValues(typeName: String, prefix: String = ""): CloseableIterator[(String, Array[Byte])]
94 
95   /**
96     * Reads all row keys from the underlying table
97     *
98     * @return matching tuples of (typeName, key)
99     */
100   protected def scanKeys(): CloseableIterator[(String, String)]
101 
102   // only synchronize if table doesn't exist - otherwise it's ready only and we can avoid synchronization
103   private val tableExists: AtomicBoolean = new AtomicBoolean(checkIfTableExists)
104 
105   private val expiry = TableBasedMetadata.Expiry.toDuration.get.toMillis
106 
107   // cache for our metadata - invalidate every 10 minutes so we keep things current
108   private val metaDataCache: LoadingCache[(String, String), Option[T]] =
109     Caffeine.newBuilder().expireAfterWrite(expiry, TimeUnit.MILLISECONDS).build(
110       new CacheLoader[(String, String), Option[T]] {
111         override def load(typeNameAndKey: (String, String)): Option[T] = {
112           if (!tableExists.get) { None } else {
113             val (typeName, key) = typeNameAndKey
114             scanValue(typeName, key).map(serializer.deserialize(typeName, _))
115           }
116         }
117       }
118     )
119 
120   // keep a separate cache for scan queries vs point lookups, so that the point lookups don't cache
121   // partial values for a scan result
122   private val metaDataScanCache: LoadingCache[(String, String), Seq[(String, T)]] =
123     Caffeine.newBuilder().expireAfterWrite(expiry, TimeUnit.MILLISECONDS).build(
124       new CacheLoader[(String, String), Seq[(String, T)]] {
125         override def load(typeNameAndPrefix: (String, String)): Seq[(String, T)] = {
126           if (!tableExists.get) { Seq.empty } else {
127             val (typeName, prefix) = typeNameAndPrefix
128             WithClose(scanValues(typeName, prefix)) { iter =>
129               val builder = Seq.newBuilder[(String, T)]
130               iter.foreach { case (k, v) => builder += k -> serializer.deserialize(typeName, v) }
131               builder.result()
132             }
133           }
134         }
135       }
136     )
137 
138   private lazy val formatter =
139     new DateTimeFormatterBuilder()
140       .parseCaseInsensitive()
141       .appendValue(ChronoField.YEAR, 4)
142       .appendValue(ChronoField.MONTH_OF_YEAR, 2)
143       .appendValue(ChronoField.DAY_OF_MONTH, 2)
144       .appendLiteral('T')
145       .appendValue(ChronoField.HOUR_OF_DAY, 2)
146       .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
147       .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
148       .toFormatter(Locale.US)
149       .withZone(ZoneOffset.UTC)
150 
151   override def getFeatureTypes: Array[String] = {
152     if (!tableExists.get) { Array.empty } else {
153       WithClose(scanKeys()) { keys =>
154         keys.collect { case (typeName, key) if key == GeoMesaMetadata.AttributesKey => typeName }.toArray
155       }
156     }
157   }
158 
159   override def read(typeName: String, key: String, cache: Boolean): Option[T] = {
160     if (!cache) {
161       metaDataCache.invalidate((typeName, key))
162     }
163     metaDataCache.get((typeName, key))
164   }
165 
166   override def scan(typeName: String, prefix: String, cache: Boolean): Seq[(String, T)] = {
167     if (!cache) {
168       metaDataScanCache.invalidate((typeName, prefix))
169     }
170     metaDataScanCache.get((typeName, prefix))
171   }
172 
173   override def insert(typeName: String, key: String, value: T): Unit = insert(typeName, Map(key -> value))
174 
175   override def insert(typeName: String, kvPairs: Map[String, T]): Unit = {
176     ensureTableExists()
177     val strings = kvPairs.map { case (k, v) =>
178       metaDataCache.put((typeName, k), Option(v)) // note: side effect in map
179       (k, serializer.serialize(typeName, v))
180     }
181     write(typeName, strings.toSeq)
182     invalidate(metaDataScanCache, typeName)
183   }
184 
185   override def invalidateCache(typeName: String, key: String): Unit = {
186     metaDataCache.invalidate((typeName, key))
187     invalidate(metaDataScanCache, typeName)
188   }
189 
190   override def remove(typeName: String, key: String): Unit = remove(typeName, Seq(key))
191 
192   override def remove(typeName: String, keys: Seq[String]): Unit = {
193     if (tableExists.get) {
194       delete(typeName, keys)
195       // also remove from the cache
196       keys.foreach(k => metaDataCache.invalidate((typeName, k)))
197       invalidate(metaDataScanCache, typeName)
198     } else {
199       logger.debug(s"Trying to delete '$typeName: ${keys.mkString(", ")}' but table does not exist")
200     }
201   }
202 
203   override def delete(typeName: String): Unit = {
204     if (tableExists.get) {
205       WithClose(scanValues(typeName)) { rows =>
206         if (rows.nonEmpty) {
207           delete(typeName, rows.map(_._1).toSeq)
208         }
209       }
210     } else {
211       logger.debug(s"Trying to delete type '$typeName' but table does not exist")
212     }
213     Seq(metaDataCache, metaDataScanCache).foreach(invalidate(_, typeName))
214   }
215 
216   override def backup(typeName: String): Unit = {
217     if (tableExists.get) {
218       WithClose(scanValues(typeName)) { rows =>
219         if (rows.nonEmpty) {
220           WithClose(createEmptyBackup(DateParsing.formatInstant(Instant.now, formatter))) { metadata =>
221             metadata.ensureTableExists()
222             metadata.write(typeName, rows.toSeq)
223           }
224         }
225       }
226     } else {
227       logger.debug(s"Trying to back up type '$typeName' but table does not exist")
228     }
229   }
230 
231   // checks that the table is already created, and creates it if not
232   def ensureTableExists(): Unit = {
233     if (tableExists.compareAndSet(false, true)) {
234       createTable()
235     }
236   }
237 
238   override def resetCache(): Unit={
239     tableExists.set(checkIfTableExists)
240     metaDataCache.invalidateAll()
241     metaDataScanCache.invalidateAll()
242   }
243 
244   /**
245     * Invalidate all keys for the given feature type
246     *
247     * @param cache cache to invalidate
248     * @param typeName feature type name
249     */
250   private def invalidate(cache: Cache[(String, String), _], typeName: String): Unit = {
251     cache.asMap.asScala.keys.foreach { k =>
252       if (k._1 == typeName) {
253         cache.invalidate(k)
254       }
255     }
256   }
257 }
258 
259 object TableBasedMetadata {
260   val Expiry = SystemProperty("geomesa.metadata.expiry", "10 minutes")
261 }
Line Stmt Id Pos Tree Symbol Tests Code
103 9733 3272 - 3290 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.checkIfTableExists TableBasedMetadata.this.checkIfTableExists
103 9734 3254 - 3291 Apply java.util.concurrent.atomic.AtomicBoolean.<init> new java.util.concurrent.atomic.AtomicBoolean(TableBasedMetadata.this.checkIfTableExists)
105 9735 3316 - 3365 Select scala.concurrent.duration.Duration.toMillis TableBasedMetadata.Expiry.toDuration.get.toMillis
109 9736 3567 - 3573 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.expiry TableBasedMetadata.this.expiry
109 9737 3575 - 3596 Literal <nosymbol> MILLISECONDS
109 9747 3528 - 3943 Apply com.github.benmanes.caffeine.cache.Caffeine.build com.github.benmanes.caffeine.cache.Caffeine.newBuilder().expireAfterWrite(TableBasedMetadata.this.expiry, MILLISECONDS).build[(String, String), Option[T]]({ final class $anon extends Object with com.github.benmanes.caffeine.cache.CacheLoader[(String, String),Option[T]] { def <init>(): <$anon: com.github.benmanes.caffeine.cache.CacheLoader[(String, String),Option[T]]> = { $anon.super.<init>(); () }; override def load(typeNameAndKey: (String, String)): Option[T] = if (TableBasedMetadata.this.tableExists.get().unary_!) scala.None else { <synthetic> <artifact> private[this] val x$1: (String, String) = (typeNameAndKey: (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) => scala.Tuple2.apply[String, String](typeName, key) }; val typeName: String = x$1._1; val key: String = x$1._2; TableBasedMetadata.this.scanValue(typeName, key).map[T](((x$2: Array[Byte]) => TableBasedMetadata.this.serializer.deserialize(typeName, x$2))) } }; new $anon() })
110 9746 3611 - 3614 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.$anon.<init> new $anon()
112 9738 3747 - 3763 Select scala.Boolean.unary_! TableBasedMetadata.this.tableExists.get().unary_!
112 9739 3767 - 3771 Select scala.None scala.None
112 9740 3767 - 3771 Block scala.None scala.None
112 9745 3779 - 3919 Block <nosymbol> { <synthetic> <artifact> private[this] val x$1: (String, String) = (typeNameAndKey: (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) => scala.Tuple2.apply[String, String](typeName, key) }; val typeName: String = x$1._1; val key: String = x$1._2; TableBasedMetadata.this.scanValue(typeName, key).map[T](((x$2: Array[Byte]) => TableBasedMetadata.this.serializer.deserialize(typeName, x$2))) }
113 9741 3798 - 3798 Select scala.Tuple2._1 x$1._1
113 9742 3808 - 3808 Select scala.Tuple2._2 x$1._2
114 9743 3871 - 3906 Apply org.locationtech.geomesa.index.metadata.MetadataSerializer.deserialize TableBasedMetadata.this.serializer.deserialize(typeName, x$2)
114 9744 3842 - 3907 Apply scala.Option.map TableBasedMetadata.this.scanValue(typeName, key).map[T](((x$2: Array[Byte]) => TableBasedMetadata.this.serializer.deserialize(typeName, x$2)))
123 9748 4210 - 4216 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.expiry TableBasedMetadata.this.expiry
123 9749 4218 - 4239 Literal <nosymbol> MILLISECONDS
123 9767 4171 - 4797 Apply com.github.benmanes.caffeine.cache.Caffeine.build com.github.benmanes.caffeine.cache.Caffeine.newBuilder().expireAfterWrite(TableBasedMetadata.this.expiry, MILLISECONDS).build[(String, String), Seq[(String, T)]]({ final class $anon extends Object with com.github.benmanes.caffeine.cache.CacheLoader[(String, String),Seq[(String, T)]] { def <init>(): <$anon: com.github.benmanes.caffeine.cache.CacheLoader[(String, String),Seq[(String, T)]]> = { $anon.super.<init>(); () }; override def load(typeNameAndPrefix: (String, String)): Seq[(String, T)] = if (TableBasedMetadata.this.tableExists.get().unary_!) scala.collection.Seq.empty[Nothing] else { <synthetic> <artifact> private[this] val x$3: (String, String) = (typeNameAndPrefix: (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (prefix @ _)) => scala.Tuple2.apply[String, String](typeName, prefix) }; val typeName: String = x$3._1; val prefix: String = x$3._2; org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Seq[(String, T)]](TableBasedMetadata.this.scanValues(typeName, prefix))(((iter: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => { val builder: scala.collection.mutable.Builder[(String, T),Seq[(String, T)]] = scala.collection.Seq.newBuilder[(String, T)]; iter.foreach[scala.collection.mutable.Builder[(String, T),Seq[(String, T)]]](((x0$1: (String, Array[Byte])) => x0$1 match { case (_1: String, _2: Array[Byte])(String, Array[Byte])((k @ _), (v @ _)) => builder.+=(scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v))) })); builder.result() }))(io.this.IsCloseable.closeableIsCloseable) } }; new $anon() })
124 9766 4254 - 4257 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.$anon.<init> new $anon()
126 9750 4407 - 4423 Select scala.Boolean.unary_! TableBasedMetadata.this.tableExists.get().unary_!
126 9751 4427 - 4436 TypeApply scala.collection.generic.GenericCompanion.empty scala.collection.Seq.empty[Nothing]
126 9752 4427 - 4436 Block scala.collection.generic.GenericCompanion.empty scala.collection.Seq.empty[Nothing]
126 9765 4444 - 4773 Block <nosymbol> { <synthetic> <artifact> private[this] val x$3: (String, String) = (typeNameAndPrefix: (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (prefix @ _)) => scala.Tuple2.apply[String, String](typeName, prefix) }; val typeName: String = x$3._1; val prefix: String = x$3._2; org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Seq[(String, T)]](TableBasedMetadata.this.scanValues(typeName, prefix))(((iter: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => { val builder: scala.collection.mutable.Builder[(String, T),Seq[(String, T)]] = scala.collection.Seq.newBuilder[(String, T)]; iter.foreach[scala.collection.mutable.Builder[(String, T),Seq[(String, T)]]](((x0$1: (String, Array[Byte])) => x0$1 match { case (_1: String, _2: Array[Byte])(String, Array[Byte])((k @ _), (v @ _)) => builder.+=(scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v))) })); builder.result() }))(io.this.IsCloseable.closeableIsCloseable) }
127 9753 4463 - 4463 Select scala.Tuple2._1 x$3._1
127 9754 4473 - 4473 Select scala.Tuple2._2 x$3._2
128 9755 4523 - 4551 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.scanValues TableBasedMetadata.this.scanValues(typeName, prefix)
128 9763 4553 - 4553 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
128 9764 4513 - 4761 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Seq[(String, T)]](TableBasedMetadata.this.scanValues(typeName, prefix))(((iter: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => { val builder: scala.collection.mutable.Builder[(String, T),Seq[(String, T)]] = scala.collection.Seq.newBuilder[(String, T)]; iter.foreach[scala.collection.mutable.Builder[(String, T),Seq[(String, T)]]](((x0$1: (String, Array[Byte])) => x0$1 match { case (_1: String, _2: Array[Byte])(String, Array[Byte])((k @ _), (v @ _)) => builder.+=(scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v))) })); builder.result() }))(io.this.IsCloseable.closeableIsCloseable)
129 9756 4591 - 4618 TypeApply scala.collection.Seq.newBuilder scala.collection.Seq.newBuilder[(String, T)]
130 9757 4679 - 4714 Apply org.locationtech.geomesa.index.metadata.MetadataSerializer.deserialize TableBasedMetadata.this.serializer.deserialize(typeName, v)
130 9758 4674 - 4714 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v))
130 9759 4663 - 4714 Apply scala.collection.mutable.Builder.+= builder.+=(scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v)))
130 9760 4663 - 4714 Block scala.collection.mutable.Builder.+= builder.+=(scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v)))
130 9761 4633 - 4716 Apply scala.collection.Iterator.foreach iter.foreach[scala.collection.mutable.Builder[(String, T),Seq[(String, T)]]](((x0$1: (String, Array[Byte])) => x0$1 match { case (_1: String, _2: Array[Byte])(String, Array[Byte])((k @ _), (v @ _)) => builder.+=(scala.Predef.ArrowAssoc[String](k).->[T](TableBasedMetadata.this.serializer.deserialize(typeName, v))) }))
131 9762 4731 - 4747 Apply scala.collection.mutable.Builder.result builder.result()
152 9768 5328 - 5344 Select scala.Boolean.unary_! TableBasedMetadata.this.tableExists.get().unary_!
152 9769 5348 - 5359 ApplyToImplicitArgs scala.Array.empty scala.Array.empty[String]((ClassTag.apply[String](classOf[java.lang.String]): scala.reflect.ClassTag[String]))
152 9770 5348 - 5359 Block scala.Array.empty scala.Array.empty[String]((ClassTag.apply[String](classOf[java.lang.String]): scala.reflect.ClassTag[String]))
153 9771 5385 - 5395 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.scanKeys TableBasedMetadata.this.scanKeys()
153 9777 5397 - 5397 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
153 9778 5375 - 5520 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, String)], Array[String]](TableBasedMetadata.this.scanKeys())(((keys: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, String)]) => keys.collect[String](({ @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[(String, String),String] with Serializable { def <init>(): <$anon: ((String, String)) => String> = { $anonfun.super.<init>(); () }; final override def applyOrElse[A1 <: (String, String), B1 >: String](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[(String, String)]: (String, String)): (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) if key.==(GeoMesaMetadata.AttributesKey) => typeName case (defaultCase$ @ _) => default.apply(x1) }; final def isDefinedAt(x1: (String, String)): Boolean = ((x1.asInstanceOf[(String, String)]: (String, String)): (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) if key.==(GeoMesaMetadata.AttributesKey) => true case (defaultCase$ @ _) => false } }; new $anonfun() }: PartialFunction[(String, String),String])).toArray[String]((ClassTag.apply[String](classOf[java.lang.String]): scala.reflect.ClassTag[String]))))(io.this.IsCloseable.closeableIsCloseable)
153 9779 5375 - 5520 Block org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, String)], Array[String]](TableBasedMetadata.this.scanKeys())(((keys: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, String)]) => keys.collect[String](({ @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[(String, String),String] with Serializable { def <init>(): <$anon: ((String, String)) => String> = { $anonfun.super.<init>(); () }; final override def applyOrElse[A1 <: (String, String), B1 >: String](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[(String, String)]: (String, String)): (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) if key.==(GeoMesaMetadata.AttributesKey) => typeName case (defaultCase$ @ _) => default.apply(x1) }; final def isDefinedAt(x1: (String, String)): Boolean = ((x1.asInstanceOf[(String, String)]: (String, String)): (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) if key.==(GeoMesaMetadata.AttributesKey) => true case (defaultCase$ @ _) => false } }; new $anonfun() }: PartialFunction[(String, String),String])).toArray[String]((ClassTag.apply[String](classOf[java.lang.String]): scala.reflect.ClassTag[String]))))(io.this.IsCloseable.closeableIsCloseable)
154 9772 5461 - 5490 Select org.locationtech.geomesa.index.metadata.GeoMesaMetadata.AttributesKey GeoMesaMetadata.AttributesKey
154 9773 5454 - 5490 Apply java.lang.Object.== key.==(GeoMesaMetadata.AttributesKey)
154 9774 5494 - 5502 Ident org.locationtech.geomesa.index.metadata.TableBasedMetadata.$anonfun.typeName typeName
154 9775 5428 - 5428 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.$anonfun.<init> new $anonfun()
154 9776 5415 - 5512 ApplyToImplicitArgs scala.collection.TraversableOnce.toArray keys.collect[String](({ @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[(String, String),String] with Serializable { def <init>(): <$anon: ((String, String)) => String> = { $anonfun.super.<init>(); () }; final override def applyOrElse[A1 <: (String, String), B1 >: String](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[(String, String)]: (String, String)): (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) if key.==(GeoMesaMetadata.AttributesKey) => typeName case (defaultCase$ @ _) => default.apply(x1) }; final def isDefinedAt(x1: (String, String)): Boolean = ((x1.asInstanceOf[(String, String)]: (String, String)): (String, String) @unchecked) match { case (_1: String, _2: String)(String, String)((typeName @ _), (key @ _)) if key.==(GeoMesaMetadata.AttributesKey) => true case (defaultCase$ @ _) => false } }; new $anonfun() }: PartialFunction[(String, String),String])).toArray[String]((ClassTag.apply[String](classOf[java.lang.String]): scala.reflect.ClassTag[String]))
160 9780 5622 - 5628 Select scala.Boolean.unary_! cache.unary_!
160 9784 5618 - 5618 Literal <nosymbol> ()
160 9785 5618 - 5618 Block <nosymbol> ()
161 9781 5663 - 5678 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, key)
161 9782 5638 - 5679 Apply com.github.benmanes.caffeine.cache.Cache.invalidate TableBasedMetadata.this.metaDataCache.invalidate(scala.Tuple2.apply[String, String](typeName, key))
161 9783 5638 - 5679 Block com.github.benmanes.caffeine.cache.Cache.invalidate TableBasedMetadata.this.metaDataCache.invalidate(scala.Tuple2.apply[String, String](typeName, key))
163 9786 5708 - 5723 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, key)
163 9787 5690 - 5724 Apply com.github.benmanes.caffeine.cache.LoadingCache.get TableBasedMetadata.this.metaDataCache.get(scala.Tuple2.apply[String, String](typeName, key))
167 9788 5830 - 5836 Select scala.Boolean.unary_! cache.unary_!
167 9792 5826 - 5826 Literal <nosymbol> ()
167 9793 5826 - 5826 Block <nosymbol> ()
168 9789 5875 - 5893 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, prefix)
168 9790 5846 - 5894 Apply com.github.benmanes.caffeine.cache.Cache.invalidate TableBasedMetadata.this.metaDataScanCache.invalidate(scala.Tuple2.apply[String, String](typeName, prefix))
168 9791 5846 - 5894 Block com.github.benmanes.caffeine.cache.Cache.invalidate TableBasedMetadata.this.metaDataScanCache.invalidate(scala.Tuple2.apply[String, String](typeName, prefix))
170 9794 5927 - 5945 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, prefix)
170 9795 5905 - 5946 Apply com.github.benmanes.caffeine.cache.LoadingCache.get TableBasedMetadata.this.metaDataScanCache.get(scala.Tuple2.apply[String, String](typeName, prefix))
173 9796 6044 - 6056 Apply scala.Predef.ArrowAssoc.-> scala.Predef.ArrowAssoc[String](key).->[T](value)
173 9797 6040 - 6057 Apply scala.collection.generic.GenMapFactory.apply scala.Predef.Map.apply[String, T](scala.Predef.ArrowAssoc[String](key).->[T](value))
173 9798 6023 - 6058 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.insert TableBasedMetadata.this.insert(typeName, scala.Predef.Map.apply[String, T](scala.Predef.ArrowAssoc[String](key).->[T](value)))
176 9799 6139 - 6158 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.ensureTableExists TableBasedMetadata.this.ensureTableExists()
177 9805 6203 - 6328 Block <nosymbol> { TableBasedMetadata.this.metaDataCache.put(scala.Tuple2.apply[String, String](typeName, k), scala.Option.apply[T](v)); scala.Tuple2.apply[String, Array[Byte]](k, TableBasedMetadata.this.serializer.serialize(typeName, v)) }
177 9806 6189 - 6189 TypeApply scala.collection.immutable.Map.canBuildFrom immutable.this.Map.canBuildFrom[String, Array[Byte]]
177 9807 6177 - 6334 ApplyToImplicitArgs scala.collection.TraversableLike.map kvPairs.map[(String, Array[Byte]), scala.collection.immutable.Map[String,Array[Byte]]](((x0$1: (String, T)) => x0$1 match { case (_1: String, _2: T)(String, T)((k @ _), (v @ _)) => { TableBasedMetadata.this.metaDataCache.put(scala.Tuple2.apply[String, String](typeName, k), scala.Option.apply[T](v)); scala.Tuple2.apply[String, Array[Byte]](k, TableBasedMetadata.this.serializer.serialize(typeName, v)) } }))(immutable.this.Map.canBuildFrom[String, Array[Byte]])
178 9800 6230 - 6243 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, k)
178 9801 6245 - 6254 Apply scala.Option.apply scala.Option.apply[T](v)
178 9802 6212 - 6255 Apply com.github.benmanes.caffeine.cache.Cache.put TableBasedMetadata.this.metaDataCache.put(scala.Tuple2.apply[String, String](typeName, k), scala.Option.apply[T](v))
179 9803 6294 - 6327 Apply org.locationtech.geomesa.index.metadata.MetadataSerializer.serialize TableBasedMetadata.this.serializer.serialize(typeName, v)
179 9804 6290 - 6328 Apply scala.Tuple2.apply scala.Tuple2.apply[String, Array[Byte]](k, TableBasedMetadata.this.serializer.serialize(typeName, v))
181 9808 6355 - 6368 Select scala.collection.MapLike.toSeq strings.toSeq
181 9809 6339 - 6369 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.write TableBasedMetadata.this.write(typeName, strings.toSeq)
182 9810 6385 - 6402 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.metaDataScanCache TableBasedMetadata.this.metaDataScanCache
182 9811 6374 - 6413 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.invalidate TableBasedMetadata.this.invalidate(TableBasedMetadata.this.metaDataScanCache, typeName)
186 9812 6520 - 6535 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, key)
186 9813 6495 - 6536 Apply com.github.benmanes.caffeine.cache.Cache.invalidate TableBasedMetadata.this.metaDataCache.invalidate(scala.Tuple2.apply[String, String](typeName, key))
187 9814 6552 - 6569 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.metaDataScanCache TableBasedMetadata.this.metaDataScanCache
187 9815 6541 - 6580 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.invalidate TableBasedMetadata.this.invalidate(TableBasedMetadata.this.metaDataScanCache, typeName)
190 9816 6664 - 6672 Apply scala.collection.generic.GenericCompanion.apply scala.collection.Seq.apply[String](key)
190 9817 6647 - 6673 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.remove TableBasedMetadata.this.remove(typeName, scala.collection.Seq.apply[String](key))
193 9818 6752 - 6767 Apply java.util.concurrent.atomic.AtomicBoolean.get TableBasedMetadata.this.tableExists.get()
193 9825 6769 - 6952 Block <nosymbol> { TableBasedMetadata.this.delete(typeName, keys); keys.foreach[Unit](((k: String) => TableBasedMetadata.this.metaDataCache.invalidate(scala.Tuple2.apply[String, String](typeName, k)))); TableBasedMetadata.this.invalidate(TableBasedMetadata.this.metaDataScanCache, typeName) }
194 9819 6777 - 6799 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.delete TableBasedMetadata.this.delete(typeName, keys)
196 9820 6885 - 6898 Apply scala.Tuple2.apply scala.Tuple2.apply[String, String](typeName, k)
196 9821 6860 - 6899 Apply com.github.benmanes.caffeine.cache.Cache.invalidate TableBasedMetadata.this.metaDataCache.invalidate(scala.Tuple2.apply[String, String](typeName, k))
196 9822 6842 - 6900 Apply scala.collection.IterableLike.foreach keys.foreach[Unit](((k: String) => TableBasedMetadata.this.metaDataCache.invalidate(scala.Tuple2.apply[String, String](typeName, k))))
197 9823 6918 - 6935 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.metaDataScanCache TableBasedMetadata.this.metaDataScanCache
197 9824 6907 - 6946 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.invalidate TableBasedMetadata.this.invalidate(TableBasedMetadata.this.metaDataScanCache, typeName)
199 9826 6966 - 7060 Typed <nosymbol> (if (TableBasedMetadata.this.logger.underlying.isDebugEnabled()) TableBasedMetadata.this.logger.underlying.debug("Trying to delete \'{}: {}\' but table does not exist", (scala.Array.apply[AnyRef]((typeName: AnyRef), (keys.mkString(", "): AnyRef))((ClassTag.AnyRef: scala.reflect.ClassTag[AnyRef])): _*)) else (): Unit)
204 9827 7130 - 7145 Apply java.util.concurrent.atomic.AtomicBoolean.get TableBasedMetadata.this.tableExists.get()
205 9828 7165 - 7185 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.scanValues TableBasedMetadata.this.scanValues(typeName, TableBasedMetadata.this.scanValues$default$2)
205 9836 7187 - 7187 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
205 9837 7155 - 7292 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Unit](TableBasedMetadata.this.scanValues(typeName, TableBasedMetadata.this.scanValues$default$2))(((rows: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => if (rows.nonEmpty) TableBasedMetadata.this.delete(typeName, rows.map[String](((x$4: (String, Array[Byte])) => x$4._1)).toSeq) else ()))(io.this.IsCloseable.closeableIsCloseable)
205 9838 7155 - 7292 Block org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Unit](TableBasedMetadata.this.scanValues(typeName, TableBasedMetadata.this.scanValues$default$2))(((rows: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => if (rows.nonEmpty) TableBasedMetadata.this.delete(typeName, rows.map[String](((x$4: (String, Array[Byte])) => x$4._1)).toSeq) else ()))(io.this.IsCloseable.closeableIsCloseable)
206 9829 7209 - 7222 Select scala.collection.TraversableOnce.nonEmpty rows.nonEmpty
206 9834 7205 - 7205 Literal <nosymbol> ()
206 9835 7205 - 7205 Block <nosymbol> ()
207 9830 7262 - 7266 Select scala.Tuple2._1 x$4._1
207 9831 7253 - 7273 Select scala.collection.TraversableOnce.toSeq rows.map[String](((x$4: (String, Array[Byte])) => x$4._1)).toSeq
207 9832 7236 - 7274 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.delete TableBasedMetadata.this.delete(typeName, rows.map[String](((x$4: (String, Array[Byte])) => x$4._1)).toSeq)
207 9833 7236 - 7274 Block org.locationtech.geomesa.index.metadata.TableBasedMetadata.delete TableBasedMetadata.this.delete(typeName, rows.map[String](((x$4: (String, Array[Byte])) => x$4._1)).toSeq)
211 9839 7312 - 7387 Typed <nosymbol> (if (TableBasedMetadata.this.logger.underlying.isDebugEnabled()) TableBasedMetadata.this.logger.underlying.debug("Trying to delete type \'{}\' but table does not exist", (typeName: AnyRef)) else (): Unit)
213 9840 7402 - 7415 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.metaDataCache TableBasedMetadata.this.metaDataCache
213 9841 7417 - 7434 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.metaDataScanCache TableBasedMetadata.this.metaDataScanCache
213 9842 7444 - 7467 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.invalidate TableBasedMetadata.this.invalidate(x$5, typeName)
213 9843 7398 - 7468 Apply scala.collection.IterableLike.foreach scala.collection.Seq.apply[com.github.benmanes.caffeine.cache.LoadingCache[(String, String), _ >: Seq[(String, T)] with Option[T] <: Equals]](TableBasedMetadata.this.metaDataCache, TableBasedMetadata.this.metaDataScanCache).foreach[Unit](((x$5: com.github.benmanes.caffeine.cache.LoadingCache[(String, String), _ >: Seq[(String, T)] with Option[T] <: Equals]) => TableBasedMetadata.this.invalidate(x$5, typeName)))
217 9844 7532 - 7547 Apply java.util.concurrent.atomic.AtomicBoolean.get TableBasedMetadata.this.tableExists.get()
218 9845 7567 - 7587 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.scanValues TableBasedMetadata.this.scanValues(typeName, TableBasedMetadata.this.scanValues$default$2)
218 9858 7589 - 7589 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
218 9859 7557 - 7851 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Unit](TableBasedMetadata.this.scanValues(typeName, TableBasedMetadata.this.scanValues$default$2))(((rows: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => if (rows.nonEmpty) org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.index.metadata.TableBasedMetadata[T], Unit](TableBasedMetadata.this.createEmptyBackup(org.locationtech.geomesa.utils.text.DateParsing.formatInstant(java.time.Instant.now(), TableBasedMetadata.this.formatter)))(((metadata: org.locationtech.geomesa.index.metadata.TableBasedMetadata[T]) => { metadata.ensureTableExists(); metadata.write(typeName, rows.toSeq) }))(io.this.IsCloseable.closeableIsCloseable) else ()))(io.this.IsCloseable.closeableIsCloseable)
218 9860 7557 - 7851 Block org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])], Unit](TableBasedMetadata.this.scanValues(typeName, TableBasedMetadata.this.scanValues$default$2))(((rows: org.locationtech.geomesa.utils.collection.CloseableIterator[(String, Array[Byte])]) => if (rows.nonEmpty) org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.index.metadata.TableBasedMetadata[T], Unit](TableBasedMetadata.this.createEmptyBackup(org.locationtech.geomesa.utils.text.DateParsing.formatInstant(java.time.Instant.now(), TableBasedMetadata.this.formatter)))(((metadata: org.locationtech.geomesa.index.metadata.TableBasedMetadata[T]) => { metadata.ensureTableExists(); metadata.write(typeName, rows.toSeq) }))(io.this.IsCloseable.closeableIsCloseable) else ()))(io.this.IsCloseable.closeableIsCloseable)
219 9846 7611 - 7624 Select scala.collection.TraversableOnce.nonEmpty rows.nonEmpty
219 9856 7607 - 7607 Literal <nosymbol> ()
219 9857 7607 - 7607 Block <nosymbol> ()
220 9847 7692 - 7703 Apply java.time.Instant.now java.time.Instant.now()
220 9848 7666 - 7715 Apply org.locationtech.geomesa.utils.text.DateParsing.formatInstant org.locationtech.geomesa.utils.text.DateParsing.formatInstant(java.time.Instant.now(), TableBasedMetadata.this.formatter)
220 9849 7648 - 7716 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.createEmptyBackup TableBasedMetadata.this.createEmptyBackup(org.locationtech.geomesa.utils.text.DateParsing.formatInstant(java.time.Instant.now(), TableBasedMetadata.this.formatter))
220 9853 7718 - 7718 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.closeableIsCloseable io.this.IsCloseable.closeableIsCloseable
220 9854 7638 - 7833 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.index.metadata.TableBasedMetadata[T], Unit](TableBasedMetadata.this.createEmptyBackup(org.locationtech.geomesa.utils.text.DateParsing.formatInstant(java.time.Instant.now(), TableBasedMetadata.this.formatter)))(((metadata: org.locationtech.geomesa.index.metadata.TableBasedMetadata[T]) => { metadata.ensureTableExists(); metadata.write(typeName, rows.toSeq) }))(io.this.IsCloseable.closeableIsCloseable)
220 9855 7638 - 7833 Block org.locationtech.geomesa.utils.io.WithClose.apply org.locationtech.geomesa.utils.io.`package`.WithClose.apply[org.locationtech.geomesa.index.metadata.TableBasedMetadata[T], Unit](TableBasedMetadata.this.createEmptyBackup(org.locationtech.geomesa.utils.text.DateParsing.formatInstant(java.time.Instant.now(), TableBasedMetadata.this.formatter)))(((metadata: org.locationtech.geomesa.index.metadata.TableBasedMetadata[T]) => { metadata.ensureTableExists(); metadata.write(typeName, rows.toSeq) }))(io.this.IsCloseable.closeableIsCloseable)
221 9850 7744 - 7772 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.ensureTableExists metadata.ensureTableExists()
222 9851 7810 - 7820 Select scala.collection.TraversableOnce.toSeq rows.toSeq
222 9852 7785 - 7821 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.write metadata.write(typeName, rows.toSeq)
227 9861 7871 - 7947 Typed <nosymbol> (if (TableBasedMetadata.this.logger.underlying.isDebugEnabled()) TableBasedMetadata.this.logger.underlying.debug("Trying to back up type \'{}\' but table does not exist", (typeName: AnyRef)) else (): Unit)
233 9862 8072 - 8110 Apply java.util.concurrent.atomic.AtomicBoolean.compareAndSet TableBasedMetadata.this.tableExists.compareAndSet(false, true)
233 9865 8068 - 8068 Literal <nosymbol> ()
233 9866 8068 - 8068 Block <nosymbol> ()
234 9863 8120 - 8133 Apply org.locationtech.geomesa.index.metadata.TableBasedMetadata.createTable TableBasedMetadata.this.createTable()
234 9864 8120 - 8133 Block org.locationtech.geomesa.index.metadata.TableBasedMetadata.createTable TableBasedMetadata.this.createTable()
239 9867 8201 - 8219 Select org.locationtech.geomesa.index.metadata.TableBasedMetadata.checkIfTableExists TableBasedMetadata.this.checkIfTableExists
239 9868 8185 - 8220 Apply java.util.concurrent.atomic.AtomicBoolean.set TableBasedMetadata.this.tableExists.set(TableBasedMetadata.this.checkIfTableExists)
240 9869 8225 - 8254 Apply com.github.benmanes.caffeine.cache.Cache.invalidateAll TableBasedMetadata.this.metaDataCache.invalidateAll()
241 9870 8259 - 8292 Apply com.github.benmanes.caffeine.cache.Cache.invalidateAll TableBasedMetadata.this.metaDataScanCache.invalidateAll()
251 9871 8541 - 8552 Apply com.github.benmanes.caffeine.cache.Cache.asMap cache.asMap()
251 9877 8541 - 8652 Apply scala.collection.IterableLike.foreach scala.collection.JavaConverters.mapAsScalaConcurrentMapConverter[(String, String), _$1](cache.asMap()).asScala.keys.foreach[Unit](((k: (String, String)) => if (k._1.==(typeName)) cache.invalidate(k) else ()))
252 9872 8591 - 8607 Apply java.lang.Object.== k._1.==(typeName)
252 9875 8587 - 8587 Literal <nosymbol> ()
252 9876 8587 - 8587 Block <nosymbol> ()
253 9873 8619 - 8638 Apply com.github.benmanes.caffeine.cache.Cache.invalidate cache.invalidate(k)
253 9874 8619 - 8638 Block com.github.benmanes.caffeine.cache.Cache.invalidate cache.invalidate(k)
260 9878 8703 - 8758 Apply org.locationtech.geomesa.utils.conf.GeoMesaSystemProperties.SystemProperty.apply org.locationtech.geomesa.utils.conf.GeoMesaSystemProperties.SystemProperty.apply("geomesa.metadata.expiry", "10 minutes")