diff --git a/data-prepper-plugins/geoip-processor/build.gradle b/data-prepper-plugins/geoip-processor/build.gradle
index 916f78e2f1..b9a85deefc 100644
--- a/data-prepper-plugins/geoip-processor/build.gradle
+++ b/data-prepper-plugins/geoip-processor/build.gradle
@@ -1,15 +1,15 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
 plugins{
-    id 'de.undercouch.download' version '4.1.2'
+    id 'de.undercouch.download' version '5.5.0'
 }
 apply plugin: 'de.undercouch.download'
 
 import de.undercouch.gradle.tasks.download.Download
 
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
 dependencies {
     implementation project(':data-prepper-api')
     implementation project(path: ':data-prepper-plugins:common')
@@ -19,90 +19,64 @@ dependencies {
     implementation libs.commons.compress
     implementation 'org.mapdb:mapdb:3.0.8'
     implementation libs.commons.io
-    implementation 'software.amazon.awssdk:aws-sdk-java:2.20.67'
+    implementation 'software.amazon.awssdk:sts'
     implementation 'software.amazon.awssdk:s3-transfer-manager'
-    implementation 'software.amazon.awssdk.crt:aws-crt:0.21.17'
+    implementation 'software.amazon.awssdk.crt:aws-crt:0.29.9'
     implementation 'com.maxmind.geoip2:geoip2:4.0.1'
     implementation 'com.maxmind.db:maxmind-db:3.0.0'
     implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final'
-
     implementation libs.commons.lang3
+
     testImplementation project(':data-prepper-core')
     testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
     testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
     testImplementation project(':data-prepper-test-common')
 }
-def geoIP2='GeoIP2'
-def geoLite2= 'GeoLite2'
-task downloadFile(type: Download) {
-
-    def urls = [
-            'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2-City-Test.mmdb',
-            'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2-Country-Test.mmdb',
-            'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoLite2-ASN-Test.mmdb'
-    ]
-    def mmdbFileExtension = '.mmdb'
-    def baseDirPath =  'src/test/resources/mmdb-file/geo-lite2/'
-
-    urls.each { url ->
-        src(url)
-        dest(baseDirPath)
-        doLast {
 
-            def testFileName = url.substring(url.lastIndexOf('/') + 1)
-            def testMmdbSubString = testFileName.substring(testFileName.lastIndexOf('-'))
-            def fileName = testFileName.substring(0, testFileName.length() - testMmdbSubString.length())
+def downloadFiles = tasks.register('downloadFiles')
 
-            if(fileName.contains(geoIP2)) {
-                fileName = fileName.replace(geoIP2, geoLite2)
-            }
-            File sourceFile = file(baseDirPath+testFileName)
-            File destinationFile = file( baseDirPath+fileName+mmdbFileExtension)
-            sourceFile.renameTo(destinationFile)
+def databaseNames = [
+        'GeoLite2-City-Test',
+        'GeoLite2-Country-Test',
+        'GeoLite2-ASN-Test'
+]
 
-        }
+databaseNames.forEach { databaseName -> {
 
+    def url =  "https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/${databaseName}.mmdb"
+    def gradleName = databaseName.replaceAll('-', '')
+    def downloadTask = tasks.register("download${gradleName}", Download) {
+        src(url)
+        dest "build/resources/test/mmdb-files/geo-lite2/${databaseName}.mmdb"
+        overwrite true
     }
+    downloadFiles.get().dependsOn downloadTask
+}}
 
+def enterpriseDatabaseNames = [
+        'GeoIP2-Enterprise-Test'
+]
 
-}
-task downloadEnterpriseFile(type: Download) {
-    dependsOn downloadFile
-    def urls = [
-            'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2-Enterprise-Test.mmdb'
-    ]
-    def mmdbFileExtension = '.mmdb'
-    def baseDirPath =  'src/test/resources/mmdb-file/geo-enterprise/'
-
-    urls.each { url ->
-        src(url)
-        def testFileName = url.substring(url.lastIndexOf('/') + 1)
-        def testMmdbSubString = testFileName.substring(testFileName.lastIndexOf('-'))
-        def fileName = testFileName.substring(0, testFileName.length() - testMmdbSubString.length())
-
-        dest(baseDirPath+testFileName)
-        doLast {
-            if(fileName.contains(geoIP2)) {
-                fileName = fileName.replace(geoIP2, geoLite2)
-            }
-            File sourceFile = file(baseDirPath+testFileName)
-            File destinationFile = file( baseDirPath+fileName+mmdbFileExtension)
-            sourceFile.renameTo(destinationFile)
-        }
+enterpriseDatabaseNames.forEach { enterpriseDatabaseName -> {
 
+    def url =  "https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/${enterpriseDatabaseName}.mmdb"
+    def gradleName = enterpriseDatabaseName.replaceAll('-', '')
+    def downloadEnterpriseTask = tasks.register("download${gradleName}", Download) {
+        src(url)
+        dest "build/resources/test/mmdb-files/geo-ip2/${enterpriseDatabaseName}.mmdb"
+        overwrite true
     }
+    downloadFiles.get().dependsOn downloadEnterpriseTask
+}}
 
-}
 
-/*task processTestResources(type: Copy) {
-    dependsOn  downloadEnterpriseFile
-    from 'src/test/resources' // Source directory containing test resources
-    into 'build/resources/test' // Destination directory for processed test resources
-}*/
-tasks.test.dependsOn 'processTestResources'
-tasks.processTestResources.dependsOn 'downloadEnterpriseFile'
 test {
     useJUnitPlatform()
+    dependsOn(downloadFiles)
+}
+
+checkstyleTest {
+    dependsOn(downloadFiles)
 }
 
 jacocoTestCoverageVerification {
@@ -110,40 +84,10 @@ jacocoTestCoverageVerification {
     violationRules {
         rule {
             limit {
-                minimum = 0.1 // temporarily reduce coverage for the builds to pass
+                minimum = 0.85
             }
         }
     }
 }
 
-check.dependsOn jacocoTestCoverageVerification
-
-sourceSets {
-    integrationTest {
-        java {
-            compileClasspath += main.output + test.output
-            runtimeClasspath += main.output + test.output
-            srcDir file('src/integrationTest/java')
-        }
-        resources.srcDir file('src/integrationTest/resources')
-    }
-}
-
-configurations {
-    integrationTestImplementation.extendsFrom testImplementation
-    integrationTestRuntime.extendsFrom testRuntime
-}
-
-task integrationTest(type: Test) {
-    group = 'verification'
-    testClassesDirs = sourceSets.integrationTest.output.classesDirs
-
-    useJUnitPlatform()
-
-    classpath = sourceSets.integrationTest.runtimeClasspath
-    systemProperty 'tests.geoipProcessor.maxmindLicenseKey', System.getProperty('tests.geoipProcessor.maxmindLicenseKey')
-
-    filter {
-        includeTestsMatching '*IT'
-    }
-}
\ No newline at end of file
+check.dependsOn jacocoTestCoverageVerification
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/integrationTest/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorUrlServiceIT.java b/data-prepper-plugins/geoip-processor/src/integrationTest/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorUrlServiceIT.java
index 70a80c0684..426686e099 100644
--- a/data-prepper-plugins/geoip-processor/src/integrationTest/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorUrlServiceIT.java
+++ b/data-prepper-plugins/geoip-processor/src/integrationTest/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorUrlServiceIT.java
@@ -81,7 +81,7 @@ public void setUp() throws JsonProcessingException {
 
     public GeoIPProcessorService createObjectUnderTest() {
         // TODO: pass in geoIpServiceConfig object
-        return new GeoIPProcessorService(null);
+        return new GeoIPProcessorService(null, null, null);
     }
 
     @Test
@@ -93,7 +93,7 @@ void verify_enrichment_of_data_from_maxmind_url() throws UnknownHostException {
         if (IPValidationCheck.isPublicIpAddress(ipAddress)) {
             InetAddress inetAddress = InetAddress.getByName(ipAddress);
             //All attributes are considered by default with the null value
-            geoData = geoIPProcessorService.getGeoData(inetAddress, null);
+//            geoData = geoIPProcessorService.getGeoData(inetAddress, null);
 
             assertThat(geoData.get("country_iso_code"), equalTo("US"));
             assertThat(geoData.get("ip"), equalTo("8.8.8.8"));
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPDatabase.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPDatabase.java
new file mode 100644
index 0000000000..ff22e138b0
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPDatabase.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor;
+
+public enum GeoIPDatabase {
+    CITY,
+    COUNTRY,
+    ASN,
+    ENTERPRISE;
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPField.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPField.java
new file mode 100644
index 0000000000..5476b7ea88
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPField.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public enum GeoIPField {
+    CONTINENT_CODE("continent_code", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    CONTINENT_NAME("continent_name", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    COUNTRY_NAME("country_name", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    IS_COUNTRY_IN_EUROPEAN_UNION("is_country_in_european_union", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    COUNTRY_ISO_CODE("country_iso_code", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    COUNTRY_CONFIDENCE("country_confidence", GeoIPDatabase.ENTERPRISE),
+    REGISTERED_COUNTRY_NAME("registered_country_name", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    REGISTERED_COUNTRY_ISO_CODE("registered_country_iso_code", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    REPRESENTED_COUNTRY_NAME("represented_country_name", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    REPRESENTED_COUNTRY_ISO_CODE("represented_country_iso_code", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    REPRESENTED_COUNTRY_TYPE("represented_country_type", GeoIPDatabase.COUNTRY, GeoIPDatabase.ENTERPRISE),
+    CITY_NAME("city_name", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    CITY_CONFIDENCE("city_confidence", GeoIPDatabase.ENTERPRISE),
+    LOCATION("location", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    LATITUDE("latitude", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    LONGITUDE("longitude", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    LOCATION_ACCURACY_RADIUS("location_accuracy_radius", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    METRO_CODE("metro_code", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    TIME_ZONE("time_zone", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    POSTAL_CODE("postal_code", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    POSTAL_CODE_CONFIDENCE("postal_code_confidence", GeoIPDatabase.ENTERPRISE),
+    MOST_SPECIFIED_SUBDIVISION_NAME("most_specified_subdivision_name", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    MOST_SPECIFIED_SUBDIVISION_ISO_CODE("most_specified_subdivision_iso_code", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    MOST_SPECIFIED_SUBDIVISION_CONFIDENCE("most_specified_subdivision_confidence", GeoIPDatabase.ENTERPRISE),
+    LEAST_SPECIFIED_SUBDIVISION_NAME("least_specified_subdivision_name", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    LEAST_SPECIFIED_SUBDIVISION_ISO_CODE("least_specified_subdivision_iso_code", GeoIPDatabase.CITY, GeoIPDatabase.ENTERPRISE),
+    LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE("least_specified_subdivision_confidence", GeoIPDatabase.ENTERPRISE),
+
+    ASN("asn", GeoIPDatabase.ASN),
+    ASN_ORGANIZATION("asn_organization", GeoIPDatabase.ASN),
+    NETWORK("network", GeoIPDatabase.ASN),
+    IP("ip", GeoIPDatabase.ASN);
+
+    private final HashSet<GeoIPDatabase> geoIPDatabases;
+    private final String fieldName;
+
+    GeoIPField(final String fieldName, final GeoIPDatabase... geoIPDatabases) {
+        this.fieldName = fieldName;
+        this.geoIPDatabases = new HashSet<>(Arrays.asList(geoIPDatabases));
+    }
+
+    public static GeoIPField findByName(final String name) {
+        GeoIPField result = null;
+        for (GeoIPField geoIPField : values()) {
+            if (geoIPField.getFieldName().equalsIgnoreCase(name)) {
+                result = geoIPField;
+                break;
+            }
+        }
+        return result;
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public Set<GeoIPDatabase> getGeoIPDatabases() {
+        return geoIPDatabases;
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessor.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessor.java
index 1d04ede2e8..c81e2caa0f 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessor.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessor.java
@@ -15,7 +15,9 @@
 import org.opensearch.dataprepper.model.processor.Processor;
 import org.opensearch.dataprepper.model.record.Record;
 import org.opensearch.dataprepper.plugins.processor.configuration.EntryConfig;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.EnrichFailedException;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.exception.EnrichFailedException;
+import org.opensearch.dataprepper.plugins.processor.exception.InvalidIPAddressException;
 import org.opensearch.dataprepper.plugins.processor.extension.GeoIPProcessorService;
 import org.opensearch.dataprepper.plugins.processor.extension.GeoIpConfigSupplier;
 import org.opensearch.dataprepper.plugins.processor.utils.IPValidationCheck;
@@ -23,11 +25,15 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Implementation class of geoIP-processor plugin. It is responsible for enrichment of
@@ -47,6 +53,8 @@ public class GeoIPProcessor extends AbstractProcessor<Record<Event>, Record<Even
   private final List<String> tagsOnFailure;
   private final GeoIPProcessorService geoIPProcessorService;
   private final ExpressionEvaluator expressionEvaluator;
+  private final Map<EntryConfig, List<GeoIPField>> entryFieldsMap;
+  final Map<EntryConfig, Set<GeoIPDatabase>> entryDatabaseMap;
 
   /**
    * GeoIPProcessor constructor for initialization of required attributes
@@ -60,14 +68,20 @@ public GeoIPProcessor(final PluginMetrics pluginMetrics,
                         final GeoIpConfigSupplier geoIpConfigSupplier,
                         final ExpressionEvaluator expressionEvaluator) {
     super(pluginMetrics);
+    if (geoIpConfigSupplier.getGeoIPProcessorService().isEmpty()) {
+      throw new RuntimeException("geoip_service configuration is required when using geoip processor.");
+    }
+    this.geoIPProcessorService = geoIpConfigSupplier.getGeoIPProcessorService().get();
     this.geoIPProcessorConfig = geoIPProcessorConfig;
-    this.geoIPProcessorService = geoIpConfigSupplier.getGeoIPProcessorService();
     this.tagsOnFailure = geoIPProcessorConfig.getTagsOnFailure();
     this.expressionEvaluator = expressionEvaluator;
     this.geoIpEventsProcessed = pluginMetrics.counter(GEO_IP_EVENTS_PROCESSED);
     this.geoIpEventsFailedLookup = pluginMetrics.counter(GEO_IP_EVENTS_FAILED_LOOKUP);
     //TODO: Use the exception metric for exceptions from service
     this.geoIpEventsFailedEngineException = pluginMetrics.counter(GEO_IP_EVENTS_FAILED_ENGINE_EXCEPTION);
+
+    this.entryFieldsMap = populateGeoIPFields();
+    this.entryDatabaseMap = populateGeoIPDatabases();
   }
 
   /**
@@ -79,34 +93,62 @@ public GeoIPProcessor(final PluginMetrics pluginMetrics,
   public Collection<Record<Event>> doExecute(final Collection<Record<Event>> records) {
     Map<String, Object> geoData;
 
+    final GeoIPDatabaseReader geoIPDatabaseReader = geoIPProcessorService.getGeoIPDatabaseReader();
+    final boolean databasesExpired = geoIPDatabaseReader.isExpired();
+
     for (final Record<Event> eventRecord : records) {
       final Event event = eventRecord.getData();
+
       final String whenCondition = geoIPProcessorConfig.getWhenCondition();
 
       if (whenCondition != null && !expressionEvaluator.evaluateConditional(whenCondition, event)) {
         continue;
       }
+      geoIpEventsProcessed.increment();
+
+      // TODO: Need to decide the behaviour, right now if all databases are expired we don't enrich the data.
+      if (databasesExpired) {
+        // TODO: Finalize the tags
+        continue;
+      }
 
       boolean isEventFailedLookup = false;
-      geoIpEventsProcessed.increment();
 
       for (final EntryConfig entry : geoIPProcessorConfig.getEntries()) {
         final String source = entry.getSource();
-        final List<String> attributes = entry.getFields();
-        final String ipAddress = event.get(source, String.class);
+        final List<GeoIPField> fields = entryFieldsMap.get(entry);
+        final Set<GeoIPDatabase> databases = entryDatabaseMap.get(entry);
+        String ipAddress = null;
+        try {
+          ipAddress = event.get(source, String.class);
+        } catch (final Exception e) {
+          // add tags
+          LOG.error(DataPrepperMarkers.EVENT, "Failed to get IP address from [{}] in event: [{}]. Caused by:[{}]",
+                  source, event, e.getMessage());
+        }
+
 
         //Lookup from DB
         if (ipAddress != null && !ipAddress.isEmpty()) {
           try {
             if (IPValidationCheck.isPublicIpAddress(ipAddress)) {
-              geoData = geoIPProcessorService.getGeoData(InetAddress.getByName(ipAddress), attributes);
-              eventRecord.getData().put(entry.getTarget(), geoData);
+              geoData = geoIPDatabaseReader.getGeoData(InetAddress.getByName(ipAddress), fields, databases);
+              if (geoData.isEmpty()) {
+                isEventFailedLookup = true;
+              } else {
+                eventRecord.getData().put(entry.getTarget(), geoData);
+              }
             } else {
               isEventFailedLookup = true;
             }
-          } catch (final IOException | EnrichFailedException ex) {
+          } catch (final InvalidIPAddressException | UnknownHostException e) {
+            isEventFailedLookup = true;
+            LOG.error(DataPrepperMarkers.EVENT, "Failed to validate IP address: [{}] in event: [{}]. Caused by:[{}]",
+                    ipAddress, event, e.getMessage());
+          } catch (final EnrichFailedException e) {
             isEventFailedLookup = true;
-            LOG.error(DataPrepperMarkers.EVENT, "Failed to get Geo data for event: [{}] for the IP address [{}]", event, ipAddress, ex);
+            LOG.error(DataPrepperMarkers.EVENT, "Failed to get Geo data for event: [{}] for the IP address [{}]. Caused by:{}",
+                    event, ipAddress, e.getMessage());
           }
         } else {
           //No Enrichment.
@@ -119,21 +161,65 @@ public Collection<Record<Event>> doExecute(final Collection<Record<Event>> recor
         event.getMetadata().addTags(tagsOnFailure);
       }
     }
+    geoIPDatabaseReader.close();
     return records;
   }
 
+  private Map<EntryConfig, List<GeoIPField>> populateGeoIPFields() {
+    final Map<EntryConfig, List<GeoIPField>> entryConfigFieldsMap = new HashMap<>();
+    for (final EntryConfig entry: geoIPProcessorConfig.getEntries()) {
+      final List<String> includeFields = entry.getIncludeFields();
+      final List<String> excludeFields = entry.getExcludeFields();
+      List<GeoIPField> geoIPFields = new ArrayList<>();
+      if (includeFields != null && !includeFields.isEmpty()) {
+        for (final String field : includeFields) {
+          final GeoIPField geoIPField = GeoIPField.findByName(field);
+          if (geoIPField != null) {
+            geoIPFields.add(geoIPField);
+          }
+        }
+      } else if (excludeFields != null) {
+        final List<GeoIPField> excludeGeoIPFields = new ArrayList<>();
+        for (final String field : excludeFields) {
+          final GeoIPField geoIPField = GeoIPField.findByName(field);
+          if (geoIPField != null) {
+            excludeGeoIPFields.add(geoIPField);
+          }
+        }
+        geoIPFields = new ArrayList<>(List.of(GeoIPField.values()));
+        geoIPFields.removeAll(excludeGeoIPFields);
+      }
+      entryConfigFieldsMap.put(entry, geoIPFields);
+    }
+    return entryConfigFieldsMap;
+  }
+
+  private Map<EntryConfig, Set<GeoIPDatabase>> populateGeoIPDatabases() {
+    final Map<EntryConfig, Set<GeoIPDatabase>> entryConfigGeoIPDatabaseMap = new HashMap<>();
+    for (final EntryConfig entry : geoIPProcessorConfig.getEntries()) {
+      final List<GeoIPField> geoIPFields = entryFieldsMap.get(entry);
+      final Set<GeoIPDatabase> geoIPDatabasesToUse = new HashSet<>();
+      for (final GeoIPField geoIPField : geoIPFields) {
+        final Set<GeoIPDatabase> geoIPDatabases = geoIPField.getGeoIPDatabases();
+        geoIPDatabasesToUse.addAll(geoIPDatabases);
+      }
+      entryConfigGeoIPDatabaseMap.put(entry, geoIPDatabasesToUse);
+    }
+    return entryConfigGeoIPDatabaseMap;
+  }
+
   @Override
   public void prepareForShutdown() {
   }
 
   @Override
   public boolean isReadyForShutdown() {
+    geoIPProcessorService.shutdown();
     return true;
   }
 
   @Override
   public void shutdown() {
-    //TODO: delete mmdb files
-    LOG.info("GeoIP plugin Shutdown");
+
   }
 }
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorConfig.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorConfig.java
index 222a8698e4..35612401ca 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorConfig.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorConfig.java
@@ -18,10 +18,10 @@
  */
 public class GeoIPProcessorConfig {
 
-    @JsonProperty("entries")
+    @Valid
     @NotNull
     @Size(min = 1)
-    @Valid
+    @JsonProperty("entries")
     private List<EntryConfig> entries;
 
     @JsonProperty("tags_on_failure")
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfig.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfig.java
index c1e95373a3..d9c22d6143 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfig.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfig.java
@@ -6,12 +6,13 @@
 package org.opensearch.dataprepper.plugins.processor.configuration;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.AssertTrue;
 import jakarta.validation.constraints.NotEmpty;
 
 import java.util.List;
 
 public class EntryConfig {
-    static final String DEFAULT_TARGET = "geolocation";
+    static final String DEFAULT_TARGET = "geo";
     @JsonProperty("source")
     @NotEmpty
     private String source;
@@ -19,8 +20,11 @@ public class EntryConfig {
     @JsonProperty("target")
     private String target = DEFAULT_TARGET;
 
-    @JsonProperty("fields")
-    private List<String> fields;
+    @JsonProperty("include_fields")
+    private List<String> includeFields;
+
+    @JsonProperty("exclude_fields")
+    private List<String> excludeFields;
 
     public String getSource() {
         return source;
@@ -30,7 +34,19 @@ public String getTarget() {
         return target;
     }
 
-    public List<String> getFields() {
-        return fields;
+    public List<String> getIncludeFields() {
+        return includeFields;
+    }
+
+    public List<String> getExcludeFields() {
+        return excludeFields;
+    }
+
+    @AssertTrue(message = "include_fields and exclude_fields are mutually exclusive. include_fields or exclude_fields is required.")
+    boolean areFieldsValid() {
+        if (includeFields == null && excludeFields == null) {
+            return false;
+        }
+        return includeFields == null || excludeFields == null;
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIP2DatabaseReader.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIP2DatabaseReader.java
new file mode 100644
index 0000000000..0160918676
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIP2DatabaseReader.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.AsnResponse;
+import com.maxmind.geoip2.model.EnterpriseResponse;
+import com.maxmind.geoip2.record.City;
+import com.maxmind.geoip2.record.Continent;
+import com.maxmind.geoip2.record.Country;
+import com.maxmind.geoip2.record.Location;
+import com.maxmind.geoip2.record.Postal;
+import com.maxmind.geoip2.record.RepresentedCountry;
+import com.maxmind.geoip2.record.Subdivision;
+import org.opensearch.dataprepper.plugins.processor.GeoIPDatabase;
+import org.opensearch.dataprepper.plugins.processor.GeoIPField;
+import org.opensearch.dataprepper.plugins.processor.exception.DatabaseReaderInitializationException;
+import org.opensearch.dataprepper.plugins.processor.exception.EnrichFailedException;
+import org.opensearch.dataprepper.plugins.processor.exception.NoValidDatabaseFoundException;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPFileManager;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class GeoIP2DatabaseReader implements GeoIPDatabaseReader, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(GeoIP2DatabaseReader.class);
+    private static final String MAXMIND_GEOIP2_DATABASE_TYPE = "geoip2";
+    private static final String ENTERPRISE_DATABASE = "enterprise";
+    private final DatabaseReaderBuilder databaseReaderBuilder;
+    private final String databasePath;
+    private final int cacheSize;
+    private final AtomicInteger closeCount;
+    private final GeoIPFileManager geoIPFileManager;
+    private final AtomicBoolean isEnterpriseDatabaseExpired;
+    private DatabaseReader enterpriseDatabaseReader;
+    private Instant enterpriseDatabaseBuildDate;
+
+    public GeoIP2DatabaseReader(final DatabaseReaderBuilder databaseReaderBuilder,
+                                final GeoIPFileManager geoIPFileManager,
+                                final String databasePath, final int cacheSize) {
+        this.databaseReaderBuilder = databaseReaderBuilder;
+        this.geoIPFileManager = geoIPFileManager;
+        this.databasePath = databasePath;
+        this.cacheSize = cacheSize;
+        closeCount = new AtomicInteger(1);
+        this.isEnterpriseDatabaseExpired = new AtomicBoolean(false);
+        buildDatabaseReaders();
+    }
+
+    private void buildDatabaseReaders() {
+        try {
+            final Optional<String> enterpriseDatabaseName = getDatabaseName(ENTERPRISE_DATABASE, databasePath, MAXMIND_GEOIP2_DATABASE_TYPE);
+
+            if (enterpriseDatabaseName.isPresent()) {
+                enterpriseDatabaseReader = databaseReaderBuilder.buildReader(Path.of(
+                        databasePath + File.separator + enterpriseDatabaseName.get()), cacheSize);
+                enterpriseDatabaseBuildDate = enterpriseDatabaseReader.getMetadata().getBuildDate().toInstant();
+            }
+        } catch (final IOException ex) {
+            throw new DatabaseReaderInitializationException("Exception while creating GeoIP2 DatabaseReaders due to: " + ex.getMessage());
+        }
+
+        if (enterpriseDatabaseReader == null) {
+            throw new NoValidDatabaseFoundException("Unable to initialize GeoIP2 database, make sure it is valid.");
+        }
+    }
+    @Override
+    public Map<String, Object> getGeoData(final InetAddress inetAddress, final List<GeoIPField> fields, final Set<GeoIPDatabase> geoIPDatabases) {
+        Map<String, Object> geoData = new HashMap<>();
+
+        try {
+            if (geoIPDatabases.contains(GeoIPDatabase.ENTERPRISE)) {
+                final Optional<EnterpriseResponse> optionalEnterpriseResponse = enterpriseDatabaseReader.tryEnterprise(inetAddress);
+                optionalEnterpriseResponse.ifPresent(response -> processEnterpriseResponse(response, geoData, fields));
+            }
+
+            if (geoIPDatabases.contains(GeoIPDatabase.ASN)) {
+                final Optional<AsnResponse> asnResponse = enterpriseDatabaseReader.tryAsn(inetAddress);
+                asnResponse.ifPresent(response -> processAsnResponse(response, geoData, fields));
+            }
+
+        } catch (final GeoIp2Exception e) {
+            throw new EnrichFailedException("Address not found in database.");
+        } catch (final IOException e) {
+            throw new EnrichFailedException("Failed to close database readers gracefully. It can be due to expired databases");
+        }
+        return geoData;
+    }
+
+    private void processEnterpriseResponse(final EnterpriseResponse enterpriseResponse, final Map<String, Object> geoData, final List<GeoIPField> fields) {
+        final Continent continent = enterpriseResponse.getContinent();
+        final Country country = enterpriseResponse.getCountry();
+        final Country registeredCountry = enterpriseResponse.getRegisteredCountry();
+        final RepresentedCountry representedCountry = enterpriseResponse.getRepresentedCountry();
+
+        final City city = enterpriseResponse.getCity();
+        final Location location = enterpriseResponse.getLocation();
+        final Postal postal = enterpriseResponse.getPostal();
+        final Subdivision mostSpecificSubdivision = enterpriseResponse.getMostSpecificSubdivision();
+        final Subdivision leastSpecificSubdivision = enterpriseResponse.getLeastSpecificSubdivision();
+
+        extractContinentFields(continent, geoData, fields);
+        extractCountryFields(country, geoData, fields, true);
+        extractRegisteredCountryFields(registeredCountry, geoData, fields);
+        extractRepresentedCountryFields(representedCountry, geoData, fields);
+        extractCityFields(city, geoData, fields, true);
+        extractLocationFields(location, geoData, fields);
+        extractPostalFields(postal, geoData, fields, true);
+        extractMostSpecifiedSubdivisionFields(mostSpecificSubdivision, geoData, fields, true);
+        extractLeastSpecifiedSubdivisionFields(leastSpecificSubdivision, geoData, fields, true);
+    }
+
+    private void processAsnResponse(final AsnResponse asnResponse, final Map<String, Object> geoData, final List<GeoIPField> fields) {
+        extractAsnFields(asnResponse, geoData, fields);
+    }
+
+    @Override
+    public boolean isExpired() {
+        final Instant instant = Instant.now();
+        if (enterpriseDatabaseReader == null) {
+            return true;
+        }
+        if (isEnterpriseDatabaseExpired.get()) {
+            return true;
+        }
+        if (enterpriseDatabaseBuildDate.plus(MAX_EXPIRY_DURATION).isBefore(instant)) {
+            isEnterpriseDatabaseExpired.set(true);
+            closeReader();
+        }
+        return isEnterpriseDatabaseExpired.get();
+    }
+
+    @Override
+    public void retain() {
+        closeCount.incrementAndGet();
+    }
+
+    @Override
+    public void close() {
+        final int count = closeCount.decrementAndGet();
+        if (count == 0) {
+            LOG.debug("Closing old geoip database readers");
+            closeReader();
+        }
+    }
+
+    private void closeReader() {
+        try {
+            if (enterpriseDatabaseReader != null) {
+                enterpriseDatabaseReader.close();
+            }
+        } catch (final IOException e) {
+            LOG.debug("Failed to close Maxmind database readers due to: {}. Force closing readers.", e.getMessage());
+        }
+
+        // delete database directory
+        final File file = new File(databasePath);
+        geoIPFileManager.deleteDirectory(file);
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIPDatabaseReader.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIPDatabaseReader.java
new file mode 100644
index 0000000000..b05d67c116
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIPDatabaseReader.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+
+import com.maxmind.geoip2.model.AsnResponse;
+import com.maxmind.geoip2.record.City;
+import com.maxmind.geoip2.record.Continent;
+import com.maxmind.geoip2.record.Country;
+import com.maxmind.geoip2.record.Location;
+import com.maxmind.geoip2.record.Postal;
+import com.maxmind.geoip2.record.RepresentedCountry;
+import com.maxmind.geoip2.record.Subdivision;
+import org.opensearch.dataprepper.plugins.processor.GeoIPDatabase;
+import org.opensearch.dataprepper.plugins.processor.GeoIPField;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Interface for storing and maintaining MaxMind database readers
+ */
+
+public interface GeoIPDatabaseReader {
+    String MAXMIND_DATABASE_EXTENSION = ".mmdb";
+    Duration MAX_EXPIRY_DURATION = Duration.ofDays(30);
+    String LAT = "lat";
+    String LON = "lon";
+
+    /**
+     * Gets the geo data from the {@link com.maxmind.geoip2.DatabaseReader}
+     *
+     * @param inetAddress InetAddress
+     * @return Map of geo field and value pairs from IP address
+     *
+     * @since 2.7
+     */
+    Map<String, Object> getGeoData(InetAddress inetAddress, List<GeoIPField> fields, Set<GeoIPDatabase> geoIPDatabases);
+
+    /**
+     * Gets if the database is expired from metadata or last updated timestamp
+     *
+     * @return boolean indicating if database is expired
+     *
+     * @since 2.7
+     */
+    boolean isExpired();
+
+    /**
+     * Retains the reader which prevents from closing if it's being used
+     *
+     * @since 2.7
+     */
+    void retain();
+
+    /**
+     * Closes the reader after processing a batch
+     *
+     * @since 2.7
+     */
+    void close();
+
+    /**
+     * Enrich attributes
+     * @param geoData geoData
+     * @param fieldName fieldName
+     * @param fieldValue fieldValue
+     */
+    default void enrichData(final Map<String, Object> geoData, final String fieldName, final Object fieldValue) {
+        if (!geoData.containsKey(fieldName) && fieldValue != null) {
+            geoData.put(fieldName, fieldValue);
+        }
+    }
+
+    default Optional<String> getDatabaseName(final String database, final String databasePath, final String databaseType) {
+        final File file = new File(databasePath);
+        if (file.exists() && file.isDirectory()) {
+            final String[] list = file.list();
+            for (final String fileName: list) {
+                final String lowerCaseFileName = fileName.toLowerCase();
+                if (lowerCaseFileName.contains(database)
+                        && fileName.endsWith(MAXMIND_DATABASE_EXTENSION)
+                        && lowerCaseFileName.contains(databaseType)) {
+                    return Optional.of(fileName);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    default void extractContinentFields(final Continent continent,
+                                        final Map<String, Object> geoData,
+                                        final List<GeoIPField> fields) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case CONTINENT_CODE:
+                        enrichData(geoData, GeoIPField.CONTINENT_CODE.getFieldName(), continent.getCode());
+                        break;
+                    case CONTINENT_NAME:
+                        enrichData(geoData, GeoIPField.CONTINENT_NAME.getFieldName(), continent.getName());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.CONTINENT_CODE.getFieldName(), continent.getCode());
+            enrichData(geoData, GeoIPField.CONTINENT_NAME.getFieldName(), continent.getName());
+        }
+    }
+
+    default void extractCountryFields(final Country country,
+                                      final Map<String, Object> geoData,
+                                      final List<GeoIPField> fields,
+                                      final boolean isEnterpriseDatabase) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case COUNTRY_NAME:
+                        enrichData(geoData, GeoIPField.COUNTRY_NAME.getFieldName(), country.getName());
+                        break;
+                    case IS_COUNTRY_IN_EUROPEAN_UNION:
+                        enrichData(geoData, GeoIPField.IS_COUNTRY_IN_EUROPEAN_UNION.getFieldName(), country.isInEuropeanUnion());
+                        break;
+                    case COUNTRY_ISO_CODE:
+                        enrichData(geoData, GeoIPField.COUNTRY_ISO_CODE.getFieldName(), country.getIsoCode());
+                        break;
+                    case COUNTRY_CONFIDENCE:
+                        if (isEnterpriseDatabase)
+                            enrichData(geoData, GeoIPField.COUNTRY_CONFIDENCE.getFieldName(), country.getConfidence());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.COUNTRY_NAME.getFieldName(), country.getName());
+            enrichData(geoData, GeoIPField.IS_COUNTRY_IN_EUROPEAN_UNION.getFieldName(), country.isInEuropeanUnion());
+            enrichData(geoData, GeoIPField.COUNTRY_ISO_CODE.getFieldName(), country.getIsoCode());
+            if (isEnterpriseDatabase)
+                enrichData(geoData,GeoIPField. COUNTRY_CONFIDENCE.getFieldName(), country.getConfidence());
+        }
+    }
+
+    default void extractRegisteredCountryFields(final Country registeredCountry,
+                                                final Map<String, Object> geoData,
+                                                final List<GeoIPField> fields) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case REGISTERED_COUNTRY_NAME:
+                        enrichData(geoData, GeoIPField.REGISTERED_COUNTRY_NAME.getFieldName(), registeredCountry.getName());
+                        break;
+                    case REGISTERED_COUNTRY_ISO_CODE:
+                        enrichData(geoData, GeoIPField.REGISTERED_COUNTRY_ISO_CODE.getFieldName(), registeredCountry.getIsoCode());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.REGISTERED_COUNTRY_NAME.getFieldName(), registeredCountry.getName());
+            enrichData(geoData, GeoIPField.REGISTERED_COUNTRY_ISO_CODE.getFieldName(), registeredCountry.getIsoCode());
+        }
+    }
+
+    default void extractRepresentedCountryFields(final RepresentedCountry representedCountry,
+                                                     final Map<String, Object> geoData,
+                                                     final List<GeoIPField> fields) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case REPRESENTED_COUNTRY_NAME:
+                        enrichData(geoData, GeoIPField.REPRESENTED_COUNTRY_NAME.getFieldName(), representedCountry.getName());
+                        break;
+                    case REPRESENTED_COUNTRY_ISO_CODE:
+                        enrichData(geoData, GeoIPField.REPRESENTED_COUNTRY_ISO_CODE.getFieldName(), representedCountry.getIsoCode());
+                        break;
+                    case REPRESENTED_COUNTRY_TYPE:
+                        enrichData(geoData, GeoIPField.REPRESENTED_COUNTRY_TYPE.getFieldName(), representedCountry.getType());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.REPRESENTED_COUNTRY_NAME.getFieldName(), representedCountry.getName());
+            enrichData(geoData, GeoIPField.REPRESENTED_COUNTRY_ISO_CODE.getFieldName(), representedCountry.getIsoCode());
+            enrichData(geoData, GeoIPField.REPRESENTED_COUNTRY_TYPE.getFieldName(), representedCountry.getType());
+        }
+    }
+
+    default void extractCityFields(final City city,
+                                   final Map<String, Object> geoData,
+                                   final List<GeoIPField> fields,
+                                   final boolean isEnterpriseDatabase) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                if (field.equals(GeoIPField.CITY_NAME)) {
+                    enrichData(geoData, GeoIPField.CITY_NAME.getFieldName(), city.getName());
+                } else if (isEnterpriseDatabase && field.equals(GeoIPField.CITY_CONFIDENCE)) {
+                    enrichData(geoData, GeoIPField.CITY_CONFIDENCE.getFieldName(), city.getConfidence());
+                }
+            }
+        } else{
+            enrichData(geoData, GeoIPField.CITY_NAME.getFieldName(), city.getName());
+            if (isEnterpriseDatabase)
+                enrichData(geoData, GeoIPField.CITY_CONFIDENCE.getFieldName(), city.getConfidence());
+        }
+    }
+
+    default void extractLocationFields(final Location location,
+                                       final Map<String, Object> geoData,
+                                       final List<GeoIPField> fields) {
+        final Map<String, Object> locationObject = new HashMap<>();
+        locationObject.put(LAT, location.getLatitude());
+        locationObject.put(LON, location.getLongitude());
+
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case LOCATION:
+                        enrichData(geoData, GeoIPField.LOCATION.getFieldName(), locationObject);
+                        break;
+                    case LATITUDE:
+                        enrichData(geoData, GeoIPField.LATITUDE.getFieldName(), location.getLatitude());
+                        break;
+                    case LONGITUDE:
+                        enrichData(geoData, GeoIPField.LONGITUDE.getFieldName(), location.getLongitude());
+                        break;
+                    case METRO_CODE:
+                        enrichData(geoData, GeoIPField.METRO_CODE.getFieldName(), location.getMetroCode());
+                        break;
+                    case TIME_ZONE:
+                        enrichData(geoData, GeoIPField.TIME_ZONE.getFieldName(), location.getTimeZone());
+                        break;
+                    case LOCATION_ACCURACY_RADIUS:
+                        enrichData(geoData, GeoIPField.LOCATION_ACCURACY_RADIUS.getFieldName(), location.getAccuracyRadius());
+                        break;
+                }
+            }
+        } else{
+            // add all fields - latitude & longitude will be part of location key
+            enrichData(geoData, GeoIPField.LOCATION.getFieldName(), locationObject);
+            enrichData(geoData, GeoIPField.METRO_CODE.getFieldName(), location.getMetroCode());
+            enrichData(geoData, GeoIPField.TIME_ZONE.getFieldName(), location.getTimeZone());
+            enrichData(geoData, GeoIPField.LOCATION_ACCURACY_RADIUS.getFieldName(), location.getAccuracyRadius());
+        }
+    }
+
+    default void extractPostalFields(final Postal postal,
+                                                    final Map<String, Object> geoData,
+                                                    final List<GeoIPField> fields,
+                                                    final boolean isEnterpriseDatabase) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                if (field.equals(GeoIPField.POSTAL_CODE)) {
+                    enrichData(geoData, GeoIPField.POSTAL_CODE.getFieldName(), postal.getCode());
+                } else if (isEnterpriseDatabase && field.equals(GeoIPField.POSTAL_CODE_CONFIDENCE)) {
+                    enrichData(geoData, GeoIPField.POSTAL_CODE_CONFIDENCE.getFieldName(), postal.getConfidence());
+                }
+            }
+        } else{
+            enrichData(geoData, GeoIPField.POSTAL_CODE.getFieldName(), postal.getCode());
+            if (isEnterpriseDatabase)
+                enrichData(geoData, GeoIPField.POSTAL_CODE_CONFIDENCE.getFieldName(), postal.getConfidence());
+        }
+    }
+
+    default void extractMostSpecifiedSubdivisionFields(final Subdivision subdivision,
+                                                         final Map<String, Object> geoData,
+                                                         final List<GeoIPField> fields,
+                                                         final boolean isEnterpriseDatabase) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case MOST_SPECIFIED_SUBDIVISION_NAME:
+                        enrichData(geoData, GeoIPField.MOST_SPECIFIED_SUBDIVISION_NAME.getFieldName(), subdivision.getName());
+                        break;
+                    case MOST_SPECIFIED_SUBDIVISION_ISO_CODE:
+                        enrichData(geoData, GeoIPField.MOST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName(), subdivision.getIsoCode());
+                        break;
+                    case MOST_SPECIFIED_SUBDIVISION_CONFIDENCE:
+                        if (isEnterpriseDatabase)
+                            enrichData(geoData, GeoIPField.MOST_SPECIFIED_SUBDIVISION_CONFIDENCE.getFieldName(), subdivision.getConfidence());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.MOST_SPECIFIED_SUBDIVISION_NAME.getFieldName(), subdivision.getName());
+            enrichData(geoData, GeoIPField.MOST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName(), subdivision.getIsoCode());
+            if (isEnterpriseDatabase)
+                enrichData(geoData, GeoIPField.MOST_SPECIFIED_SUBDIVISION_CONFIDENCE.getFieldName(), subdivision.getConfidence());
+        }
+    }
+
+    default void extractLeastSpecifiedSubdivisionFields(final Subdivision subdivision,
+                                          final Map<String, Object> geoData,
+                                          final List<GeoIPField> fields,
+                                          final boolean isEnterpriseDatabase) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case LEAST_SPECIFIED_SUBDIVISION_NAME:
+                        enrichData(geoData, GeoIPField.LEAST_SPECIFIED_SUBDIVISION_NAME.getFieldName(), subdivision.getName());
+                        break;
+                    case LEAST_SPECIFIED_SUBDIVISION_ISO_CODE:
+                        enrichData(geoData, GeoIPField.LEAST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName(), subdivision.getIsoCode());
+                        break;
+                    case LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE:
+                        if (isEnterpriseDatabase)
+                            enrichData(geoData, GeoIPField.LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE.getFieldName(), subdivision.getConfidence());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.LEAST_SPECIFIED_SUBDIVISION_NAME.getFieldName(), subdivision.getName());
+            enrichData(geoData, GeoIPField.LEAST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName(), subdivision.getIsoCode());
+            if (isEnterpriseDatabase)
+                enrichData(geoData, GeoIPField.LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE.getFieldName(), subdivision.getConfidence());
+        }
+    }
+
+    default void extractAsnFields(final AsnResponse asnResponse,
+                                  final Map<String, Object> geoData,
+                                  final List<GeoIPField> fields) {
+        if (!fields.isEmpty()) {
+            for (final GeoIPField field : fields) {
+                switch (field) {
+                    case ASN:
+                        enrichData(geoData, GeoIPField.ASN.getFieldName(), asnResponse.getAutonomousSystemNumber());
+                        break;
+                    case ASN_ORGANIZATION:
+                        enrichData(geoData, GeoIPField.ASN_ORGANIZATION.getFieldName(), asnResponse.getAutonomousSystemOrganization());
+                        break;
+                    case NETWORK:
+                        enrichData(geoData, GeoIPField.NETWORK.getFieldName(), asnResponse.getNetwork().toString());
+                        break;
+                    case IP:
+                        enrichData(geoData, GeoIPField.IP.getFieldName(), asnResponse.getIpAddress());
+                        break;
+                }
+            }
+        } else {
+            // add all fields
+            enrichData(geoData, GeoIPField.ASN.getFieldName(), asnResponse.getAutonomousSystemNumber());
+            enrichData(geoData, GeoIPField.ASN_ORGANIZATION.getFieldName(), asnResponse.getAutonomousSystemOrganization());
+            enrichData(geoData, GeoIPField.NETWORK.getFieldName(), asnResponse.getNetwork().toString());
+            enrichData(geoData, GeoIPField.IP.getFieldName(), asnResponse.getIpAddress());
+        }
+    }
+
+
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoLite2DatabaseReader.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoLite2DatabaseReader.java
new file mode 100644
index 0000000000..cf4778978b
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoLite2DatabaseReader.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.AsnResponse;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.model.CountryResponse;
+import com.maxmind.geoip2.record.City;
+import com.maxmind.geoip2.record.Continent;
+import com.maxmind.geoip2.record.Country;
+import com.maxmind.geoip2.record.Location;
+import com.maxmind.geoip2.record.Postal;
+import com.maxmind.geoip2.record.RepresentedCountry;
+import com.maxmind.geoip2.record.Subdivision;
+import org.opensearch.dataprepper.plugins.processor.GeoIPDatabase;
+import org.opensearch.dataprepper.plugins.processor.GeoIPField;
+import org.opensearch.dataprepper.plugins.processor.exception.DatabaseReaderInitializationException;
+import org.opensearch.dataprepper.plugins.processor.exception.EnrichFailedException;
+import org.opensearch.dataprepper.plugins.processor.exception.NoValidDatabaseFoundException;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPFileManager;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class GeoLite2DatabaseReader implements GeoIPDatabaseReader, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(GeoLite2DatabaseReader.class);
+    static final String MAXMIND_GEOLITE2_DATABASE_TYPE = "geolite2";
+    static final String CITY_DATABASE = "city";
+    static final String COUNTRY_DATABASE = "country";
+    static final String ASN_DATABASE = "asn";
+    private final DatabaseReaderBuilder databaseReaderBuilder;
+    private final String databasePath;
+    private final int cacheSize;
+    private final AtomicInteger closeCount;
+    private final AtomicBoolean isCountryDatabaseExpired;
+    private final AtomicBoolean isCityDatabaseExpired;
+    private final AtomicBoolean isAsnDatabaseExpired;
+    private final GeoIPFileManager geoIPFileManager;
+    private DatabaseReader cityDatabaseReader;
+    private DatabaseReader countryDatabaseReader;
+    private DatabaseReader asnDatabaseReader;
+    private Instant cityDatabaseBuildDate;
+    private Instant countryDatabaseBuildDate;
+    private Instant asnDatabaseBuildDate;
+
+    public GeoLite2DatabaseReader(final DatabaseReaderBuilder databaseReaderBuilder,
+                                  final GeoIPFileManager geoIPFileManager,
+                                  final String databasePath, final int cacheSize) {
+        this.databaseReaderBuilder = databaseReaderBuilder;
+        this.geoIPFileManager = geoIPFileManager;
+        this.databasePath = databasePath;
+        this.cacheSize = cacheSize;
+        this.closeCount = new AtomicInteger(1);
+        this.isCountryDatabaseExpired = new AtomicBoolean(false);
+        this.isCityDatabaseExpired = new AtomicBoolean(false);
+        this.isAsnDatabaseExpired = new AtomicBoolean(false);
+        buildDatabaseReaders();
+    }
+
+    private void buildDatabaseReaders() {
+        try {
+            final Optional<String> cityDatabaseName = getDatabaseName(CITY_DATABASE, databasePath, MAXMIND_GEOLITE2_DATABASE_TYPE);
+            final Optional<String> countryDatabaseName = getDatabaseName(COUNTRY_DATABASE, databasePath, MAXMIND_GEOLITE2_DATABASE_TYPE);
+            final Optional<String> asnDatabaseName = getDatabaseName(ASN_DATABASE, databasePath, MAXMIND_GEOLITE2_DATABASE_TYPE);
+
+            if (cityDatabaseName.isPresent()) {
+                cityDatabaseReader = databaseReaderBuilder.buildReader(Path.of(databasePath + File.separator + cityDatabaseName.get()), cacheSize);
+                cityDatabaseBuildDate = cityDatabaseReader.getMetadata().getBuildDate().toInstant();
+            }
+            if (countryDatabaseName.isPresent()) {
+                countryDatabaseReader = databaseReaderBuilder.buildReader(Path.of(databasePath + File.separator + countryDatabaseName.get()), cacheSize);
+                countryDatabaseBuildDate = countryDatabaseReader.getMetadata().getBuildDate().toInstant();
+            }
+            if (asnDatabaseName.isPresent()) {
+                asnDatabaseReader = databaseReaderBuilder.buildReader(Path.of(databasePath + File.separator + asnDatabaseName.get()), cacheSize);
+                asnDatabaseBuildDate = asnDatabaseReader.getMetadata().getBuildDate().toInstant();
+            }
+
+        } catch (final IOException ex) {
+            throw new DatabaseReaderInitializationException("Exception while creating GeoLite2 DatabaseReaders due to: " + ex.getMessage());
+        }
+
+        if (cityDatabaseReader == null && countryDatabaseReader == null && asnDatabaseReader == null) {
+            throw new NoValidDatabaseFoundException("Unable to initialize any GeoLite2 database, make sure they are valid.");
+        }
+    }
+
+    @Override
+    public Map<String, Object> getGeoData(final InetAddress inetAddress, final List<GeoIPField> fields, final Set<GeoIPDatabase> geoIPDatabases) {
+        final Map<String, Object> geoData = new HashMap<>();
+
+        try {
+            if (geoIPDatabases.contains(GeoIPDatabase.COUNTRY)) {
+                final Optional<CountryResponse> countryResponse = countryDatabaseReader.tryCountry(inetAddress);
+                countryResponse.ifPresent(response -> processCountryResponse(response, geoData, fields));
+            }
+
+            if (geoIPDatabases.contains(GeoIPDatabase.CITY)) {
+                final Optional<CityResponse> cityResponse = cityDatabaseReader.tryCity(inetAddress);
+                cityResponse.ifPresent(response -> processCityResponse(response, geoData, fields, geoIPDatabases));
+            }
+
+            if (geoIPDatabases.contains(GeoIPDatabase.ASN)) {
+                final Optional<AsnResponse> asnResponse = asnDatabaseReader.tryAsn(inetAddress);
+                asnResponse.ifPresent(response -> processAsnResponse(response, geoData, fields));
+            }
+
+        } catch (final GeoIp2Exception e) {
+            throw new EnrichFailedException("Address not found in database.");
+        } catch (final IOException e) {
+            throw new EnrichFailedException("Failed to close database readers gracefully. It can be due to expired databases.");
+        }
+        return geoData;
+    }
+
+    private void processCountryResponse(final CountryResponse countryResponse, final Map<String, Object> geoData, final List<GeoIPField> fields) {
+        final Continent continent = countryResponse.getContinent();
+        final Country country = countryResponse.getCountry();
+        final Country registeredCountry = countryResponse.getRegisteredCountry();
+        final RepresentedCountry representedCountry = countryResponse.getRepresentedCountry();
+
+
+        extractContinentFields(continent, geoData, fields);
+        extractCountryFields(country, geoData, fields, false);
+        extractRegisteredCountryFields(registeredCountry, geoData, fields);
+        extractRepresentedCountryFields(representedCountry, geoData, fields);
+    }
+
+    private void processCityResponse(final CityResponse cityResponse,
+                                     final Map<String, Object> geoData,
+                                     final List<GeoIPField> fields,
+                                     final Set<GeoIPDatabase> geoIPDatabases) {
+        // Continent and Country fields are added from City database only if they are not extracted from Country database
+        if (!geoIPDatabases.contains(GeoIPDatabase.COUNTRY)) {
+            final Continent continent = cityResponse.getContinent();
+            final Country country = cityResponse.getCountry();
+            final Country registeredCountry = cityResponse.getRegisteredCountry();
+            final RepresentedCountry representedCountry = cityResponse.getRepresentedCountry();
+
+            extractContinentFields(continent, geoData, fields);
+            extractCountryFields(country, geoData, fields, false);
+            extractRegisteredCountryFields(registeredCountry, geoData, fields);
+            extractRepresentedCountryFields(representedCountry, geoData, fields);
+        }
+
+        final City city = cityResponse.getCity();
+        final Location location = cityResponse.getLocation();
+        final Postal postal = cityResponse.getPostal();
+        final Subdivision mostSpecificSubdivision = cityResponse.getMostSpecificSubdivision();
+        final Subdivision leastSpecificSubdivision = cityResponse.getLeastSpecificSubdivision();
+
+        extractCityFields(city, geoData, fields, false);
+        extractLocationFields(location, geoData, fields);
+        extractPostalFields(postal, geoData, fields, false);
+        extractMostSpecifiedSubdivisionFields(mostSpecificSubdivision, geoData, fields, false);
+        extractLeastSpecifiedSubdivisionFields(leastSpecificSubdivision, geoData, fields, false);
+    }
+
+    private void processAsnResponse(final AsnResponse asnResponse, final Map<String, Object> geoData, final List<GeoIPField> fields) {
+        extractAsnFields(asnResponse, geoData, fields);
+    }
+
+    @Override
+    public void retain() {
+        closeCount.incrementAndGet();
+    }
+
+    @Override
+    public void close() {
+        final int count = closeCount.decrementAndGet();
+        if (count == 0) {
+            LOG.debug("Closing old geoip database readers");
+            closeReaders();
+        }
+    }
+
+    @Override
+    public boolean isExpired() {
+        // TODO: Decide the expiry behaviour
+        final Instant instant = Instant.now();
+        return isDatabaseExpired(instant, countryDatabaseReader, isCountryDatabaseExpired, countryDatabaseBuildDate, COUNTRY_DATABASE) &&
+                isDatabaseExpired(instant, cityDatabaseReader, isCityDatabaseExpired, cityDatabaseBuildDate, CITY_DATABASE) &&
+                isDatabaseExpired(instant, asnDatabaseReader, isAsnDatabaseExpired, asnDatabaseBuildDate, ASN_DATABASE);
+    }
+
+    private boolean isDatabaseExpired(final Instant instant,
+                                      final DatabaseReader databaseReader,
+                                      final AtomicBoolean isDatabaseExpired,
+                                      final Instant databaseBuildDate,
+                                      final String databaseName) {
+        if (databaseReader == null) {
+            // no need to delete - no action needed
+            return true;
+        }
+        if (isDatabaseExpired.get()) {
+            // Another thread already updated status to expired - no action needed
+            return true;
+        }
+        if (databaseBuildDate.plus(MAX_EXPIRY_DURATION).isBefore(instant)) {
+            isDatabaseExpired.set(true);
+            closeReader(databaseReader, databaseName);
+        }
+        return isDatabaseExpired.get();
+    }
+
+    private void closeReaders() {
+        try {
+            if (cityDatabaseReader != null) {
+                cityDatabaseReader.close();
+            }
+            if (countryDatabaseReader != null) {
+                countryDatabaseReader.close();
+            }
+            if (asnDatabaseReader != null) {
+                asnDatabaseReader.close();
+            }
+        } catch (final IOException e) {
+            LOG.debug("Failed to close Maxmind database readers due to: {}. Force closing readers.", e.getMessage());
+        }
+
+        // delete database directory
+        final File file = new File(databasePath);
+        geoIPFileManager.deleteDirectory(file);
+    }
+
+    private void closeReader(final DatabaseReader databaseReader, final String databaseName) {
+        try {
+            if (databaseReader != null) {
+                databaseReader.close();
+            }
+        } catch (final IOException e) {
+            LOG.debug("Failed to close Maxmind database readers due to: {}. Force closing readers.", e.getMessage());
+        }
+
+        // delete database file
+        final Optional<String> fileName = getDatabaseName(databaseName, databasePath, MAXMIND_GEOLITE2_DATABASE_TYPE);
+        fileName.ifPresent(response -> {
+            File file = new File(databasePath + File.separator + response);
+            geoIPFileManager.deleteFile(file);
+        });
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoData.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoData.java
deleted file mode 100644
index 3730be7990..0000000000
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoData.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
-
-import java.net.InetAddress;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Interface for Downloading through S3 or URl or from local path
- */
-public interface GetGeoData {
-
-    public final String GeoLite2CityDB = "GeoLite2-City.mmdb";
-    public final String GeoLite2CountryDB = "GeoLite2-Country.mmdb";
-    public final String GeoLite2AsnDB = "GeoLite2-ASN.mmdb";
-    public final String GeoIP2EnterpriseDB = "GeoIP2-Enterprise.mmdb";
-
-    void switchDatabaseReader();
-    void closeReader();
-    Map<String, Object> getGeoData(InetAddress inetAddress, List<String> attributes, String tempDestDir);
-
-    /**
-     * Enrich attributes
-     * @param geoData geoData
-     * @param attributeName attributeName
-     * @param attributeValue attributeValue
-     */
-    default void enrichData(final Map<String, Object> geoData, final String attributeName, final String attributeValue) {
-        if (attributeValue != null) {
-            geoData.put(attributeName, attributeValue);
-        }
-    }
-
-    /**
-     * Enrich region iso code
-     * @param geoData geoData
-     * @param countryIso countryIso
-     * @param subdivisionIso subdivisionIso
-     */
-    default void enrichRegionIsoCode(final Map<String, Object> geoData, final String countryIso, final String subdivisionIso) {
-       if (countryIso != null && subdivisionIso != null) {
-           enrichData(geoData, "region_iso_code", countryIso + "-" + subdivisionIso);
-       }
-    }
-
-    /**
-     * Enrich Location Data
-     * @param geoData geoData
-     * @param latitude latitude
-     * @param longitude longitude
-     */
-    default void enrichLocationData(final Map<String, Object> geoData, final Double latitude, final Double longitude) {
-        if (latitude != null && longitude != null) {
-            Map<String, Object> locationObject = new HashMap<>();
-            locationObject.put("lat", latitude);
-            locationObject.put("lon", longitude);
-            geoData.put("location", locationObject);
-        }
-    }
-}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoIP2Data.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoIP2Data.java
deleted file mode 100644
index 70d888f587..0000000000
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoIP2Data.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
-
-import com.maxmind.geoip2.DatabaseReader;
-import com.maxmind.geoip2.exception.GeoIp2Exception;
-import com.maxmind.geoip2.model.EnterpriseResponse;
-import com.maxmind.geoip2.record.City;
-import com.maxmind.geoip2.record.Continent;
-import com.maxmind.geoip2.record.Country;
-import com.maxmind.geoip2.record.Subdivision;
-import com.maxmind.geoip2.record.Location;
-import com.maxmind.geoip2.record.Postal;
-import org.opensearch.dataprepper.plugins.processor.extension.GeoIPProcessorService;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderCreate;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Implementation class for enrichment of enterprise data
- */
-public class GetGeoIP2Data implements GetGeoData {
-
-    private static final Logger LOG = LoggerFactory.getLogger(GetGeoIP2Data.class);
-    public static final String COUNTRY_NAME = "country_name";
-    public static final String CONTINENT_NAME = "continent_name";
-    public static final String REGION_NAME = "region_name";
-    public static final String CITY_NAME = "city_name";
-    public static final String COUNTRY_ISO_CODE = "country_iso_code";
-    public static final String IP = "ip";
-    public static final String REGION_ISO_CODE = "region_iso_code";
-    public static final String TIMEZONE = "timezone";
-    public static final String LOCATION = "location";
-    public static final String POSTAL = "postal";
-    private DatabaseReader readerEnterprise;
-    private Country country;
-    private Continent continent;
-    private City city;
-    private Location location;
-    private Subdivision subdivision;
-    private String dbPath;
-    private int cacheSize;
-    private Postal postal;
-    private String tempDestDir;
-
-    /**
-     * GetGeoLite2Data constructor for initialisation of attributes
-     * @param dbPath dbPath
-     * @param cacheSize cacheSize
-     */
-    public GetGeoIP2Data(final String dbPath, final int cacheSize) {
-        this.dbPath = dbPath;
-        this.cacheSize = cacheSize;
-        initDatabaseReader();
-    }
-
-    /**
-     * Initialise all the DatabaseReader
-     */
-    public void initDatabaseReader() {
-        try {
-            readerEnterprise = DatabaseReaderCreate.createLoader(Path.of(dbPath + File.separator + tempDestDir + File.separator + GeoIP2EnterpriseDB), cacheSize);
-        } catch (final IOException ex) {
-            LOG.error("Exception while creating GeoIP2 DatabaseReader: {0}", ex);
-        }
-    }
-
-    /**
-     * Switch all the DatabaseReader
-     */
-    @Override
-    public void switchDatabaseReader() {
-        LOG.info("Switching GeoIP2 DatabaseReader");
-        closeReader();
-        System.gc();
-        File file = new File(dbPath);
-        DBSource.deleteDirectory(file);
-        initDatabaseReader();
-    }
-
-    /**
-     * Enrich the GeoData
-     * @param inetAddress inetAddress
-     * @param attributes attributes
-     * @return enriched data Map
-     */
-    public Map<String, Object> getGeoData(InetAddress inetAddress, List<String> attributes, String tempDestDir) {
-        Map<String, Object> geoData = new HashMap<>();
-        if (GeoIPProcessorService.downloadReady) {
-            this.tempDestDir = tempDestDir;
-            GeoIPProcessorService.downloadReady = false;
-            switchDatabaseReader();
-        }
-        try {
-            EnterpriseResponse enterpriseResponse = readerEnterprise.enterprise(inetAddress);
-            country = enterpriseResponse.getCountry();
-            subdivision = enterpriseResponse.getMostSpecificSubdivision();
-            city = enterpriseResponse.getCity();
-            location = enterpriseResponse.getLocation();
-            continent = enterpriseResponse.getContinent();
-            postal = enterpriseResponse.getPostal();
-        } catch (IOException | GeoIp2Exception ex) {
-            LOG.info("Look up Exception : {0}",  ex);
-        }
-
-        try {
-            if ((attributes != null) && (!attributes.isEmpty())) {
-                for (String attribute : attributes) {
-                    switch (attribute) {
-                        case IP:
-                            enrichData(geoData, IP, inetAddress.getHostAddress());
-                            break;
-                        case COUNTRY_ISO_CODE:
-                            enrichData(geoData, COUNTRY_ISO_CODE, country.getIsoCode());
-                            break;
-                        case COUNTRY_NAME:
-                            enrichData(geoData, COUNTRY_NAME, country.getName());
-                            break;
-                        case CONTINENT_NAME:
-                            enrichData(geoData, CONTINENT_NAME, continent.getName());
-                            break;
-                        case REGION_ISO_CODE:
-                            // ISO 3166-2 code for country subdivisions.
-                            // See iso.org/iso-3166-country-codes.html
-                            enrichRegionIsoCode(geoData, country.getIsoCode(), subdivision.getIsoCode());
-                            break;
-                        case REGION_NAME:
-                            enrichData(geoData, REGION_NAME, subdivision.getName());
-                            break;
-                        case CITY_NAME:
-                            enrichData(geoData, CITY_NAME, city.getName());
-                            break;
-                        case TIMEZONE:
-                            enrichData(geoData, TIMEZONE, location.getTimeZone());
-                            break;
-                        case LOCATION:
-                            enrichLocationData(geoData, location.getLatitude(), location.getLongitude());
-                            break;
-                        case POSTAL:
-                            enrichData(geoData, "postalCode", postal.getCode());
-                            break;
-                    }
-                }
-            } else {
-
-                enrichData(geoData, IP, inetAddress.getHostAddress());
-                enrichData(geoData, COUNTRY_ISO_CODE, country.getIsoCode());
-                enrichData(geoData, COUNTRY_NAME, country.getName());
-                enrichData(geoData, CONTINENT_NAME, continent.getName());
-
-                enrichRegionIsoCode(geoData, country.getIsoCode(), subdivision.getIsoCode());
-
-                enrichData(geoData, REGION_NAME, subdivision.getName());
-                enrichData(geoData, CITY_NAME, city.getName());
-                enrichData(geoData, "postalCode", postal.getCode());
-
-                enrichData(geoData, TIMEZONE, location.getTimeZone());
-                enrichLocationData(geoData, location.getLatitude(), location.getLongitude());
-            }
-        } catch (Exception ex) {
-            throw new EnrichFailedException("Enrichment failed exception" + ex);
-        }
-        return geoData;
-    }
-
-
-    /**
-     * Close the DatabaseReader
-     */
-    @Override
-    public void closeReader() {
-        try {
-            if (readerEnterprise != null)
-                readerEnterprise.close();
-        } catch (IOException ex) {
-            LOG.info("Close Enterprise DatabaseReader Exception : {0}",  ex);
-        }
-    }
-}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoLite2Data.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoLite2Data.java
deleted file mode 100644
index 0d719541e0..0000000000
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoLite2Data.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
-
-import com.maxmind.db.Network;
-import com.maxmind.geoip2.DatabaseReader;
-import com.maxmind.geoip2.exception.GeoIp2Exception;
-import com.maxmind.geoip2.model.AsnResponse;
-import com.maxmind.geoip2.model.CityResponse;
-import com.maxmind.geoip2.model.CountryResponse;
-import com.maxmind.geoip2.record.City;
-import com.maxmind.geoip2.record.Continent;
-import com.maxmind.geoip2.record.Country;
-import com.maxmind.geoip2.record.Subdivision;
-import com.maxmind.geoip2.record.Location;
-import org.opensearch.dataprepper.plugins.processor.extension.GeoIPProcessorService;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSource;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderCreate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Implementation class for enrichment of geoip lite2 data
- */
-public class GetGeoLite2Data implements GetGeoData {
-
-    private static final Logger LOG = LoggerFactory.getLogger(GetGeoLite2Data.class);
-    public static final String COUNTRY_NAME = "country_name";
-    public static final String CONTINENT_NAME = "continent_name";
-    public static final String REGION_NAME = "region_name";
-    public static final String CITY_NAME = "city_name";
-    public static final String ORGANIZATION_NAME = "organization_name";
-    public static final String NETWORK = "network";
-    public static final String COUNTRY_ISO_CODE = "country_iso_code";
-    public static final String IP = "ip";
-    public static final String REGION_ISO_CODE = "region_iso_code";
-    public static final String TIMEZONE = "timezone";
-    public static final String LOCATION = "location";
-    public static final String ASN = "asn";
-    private DatabaseReader readerCity;
-    private DatabaseReader readerCountry;
-    private DatabaseReader readerAsn;
-    private Country country;
-    private Continent continent;
-    private City city;
-    private Location location;
-    private Subdivision subdivision;
-    private Long asn;
-    private String organizationName;
-    private Network network;
-    private String dbPath;
-    private int cacheSize;
-    private CityResponse responseCity;
-    private CountryResponse responseCountry;
-    private AsnResponse responseAsn;
-    private String tempDestDir;
-
-
-    /**
-     * GetGeoLite2Data constructor for initialisation of attributes
-     * @param dbPath dbPath
-     * @param cacheSize cacheSize
-     */
-    public GetGeoLite2Data(final String dbPath, final int cacheSize) {
-        this.dbPath = dbPath;
-        this.cacheSize = cacheSize;
-        initDatabaseReader();
-    }
-
-    /**
-     * Initialise all the DatabaseReader
-     */
-    private void initDatabaseReader() {
-        try {
-            readerCity = DatabaseReaderCreate.createLoader(Path.of(dbPath + File.separator + GeoLite2CityDB), cacheSize);
-            readerCountry = DatabaseReaderCreate.createLoader(Path.of(dbPath + File.separator + GeoLite2CountryDB), cacheSize);
-            readerAsn = DatabaseReaderCreate.createLoader(Path.of(dbPath + File.separator + GeoLite2AsnDB), cacheSize);
-        } catch (final IOException ex) {
-            LOG.error("Exception while creating GeoLite2 DatabaseReader: {0}", ex);
-        }
-    }
-
-    /**
-     * Switch all the DatabaseReader
-     */
-    @Override
-    public void switchDatabaseReader() {
-        LOG.info("Switching GeoLite2 DatabaseReaders");
-        closeReaderCity();
-        closeReaderCountry();
-        closeReaderAsn();
-        System.gc();
-        final File file = new File(dbPath);
-        DBSource.deleteDirectory(file);
-        dbPath = tempDestDir;
-        initDatabaseReader();
-    }
-
-    /**
-     * Enrich the GeoData
-     * @param inetAddress inetAddress
-     * @param attributes attributes
-     * @return enriched data Map
-     */
-    @Override
-    public Map<String, Object> getGeoData(final InetAddress inetAddress, final List<String> attributes, final String tempDestDir) {
-        Map<String, Object> geoData = new HashMap<>();
-        if (GeoIPProcessorService.downloadReady) {
-            this.tempDestDir = tempDestDir;
-            GeoIPProcessorService.downloadReady = false;
-            switchDatabaseReader();
-        }
-        try {
-            responseCountry = readerCountry.country(inetAddress);
-            country = responseCountry.getCountry();
-            continent = responseCountry.getContinent();
-
-            responseCity = readerCity.city(inetAddress);
-            city = responseCity.getCity();
-            location = responseCity.getLocation();
-            subdivision = responseCity.getMostSpecificSubdivision();
-
-            responseAsn = readerAsn.asn(inetAddress);
-            asn = responseAsn.getAutonomousSystemNumber();
-            organizationName = responseAsn.getAutonomousSystemOrganization();
-            network = responseAsn.getNetwork();
-        } catch (IOException | GeoIp2Exception ex) {
-            LOG.info("Look up Exception : {0}",  ex);
-        }
-
-        try {
-            if ((attributes != null) && (!attributes.isEmpty())) {
-                for (String attribute : attributes) {
-                    switch (attribute) {
-                        case IP:
-                            enrichData(geoData, IP, inetAddress.getHostAddress());
-                            break;
-                        case COUNTRY_ISO_CODE:
-                            enrichData(geoData, COUNTRY_ISO_CODE, country.getIsoCode());
-                            break;
-                        case COUNTRY_NAME:
-                            enrichData(geoData, COUNTRY_NAME, country.getName());
-                            break;
-                        case CONTINENT_NAME:
-                            enrichData(geoData, CONTINENT_NAME, continent.getName());
-                            break;
-                        case REGION_ISO_CODE:
-                            // ISO 3166-2 code for country subdivisions.
-                            // See iso.org/iso-3166-country-codes.html
-                            enrichRegionIsoCode(geoData, country.getIsoCode(), subdivision.getIsoCode());
-                            break;
-                        case REGION_NAME:
-                            enrichData(geoData, REGION_NAME, subdivision.getName());
-                            break;
-                        case CITY_NAME:
-                            enrichData(geoData, CITY_NAME, city.getName());
-                            break;
-                        case TIMEZONE:
-                            enrichData(geoData, TIMEZONE, location.getTimeZone());
-                            break;
-                        case LOCATION:
-                            enrichLocationData(geoData, location.getLatitude(), location.getLongitude());
-                            break;
-                        case ASN:
-                            if (asn != null) {
-                                geoData.put(ASN, asn);
-                            }
-                            break;
-                        case ORGANIZATION_NAME:
-                            enrichData(geoData, ORGANIZATION_NAME, organizationName);
-                            break;
-                        case NETWORK:
-                            enrichData(geoData, NETWORK,network!=null? network.toString():null);
-                            break;
-                    }
-                }
-            } else {
-
-                enrichData(geoData, IP, inetAddress.getHostAddress());
-                enrichData(geoData, COUNTRY_ISO_CODE, country.getIsoCode());
-                enrichData(geoData, COUNTRY_NAME, country.getName());
-                enrichData(geoData, CONTINENT_NAME, continent.getName());
-                enrichRegionIsoCode(geoData, country.getIsoCode(), subdivision.getIsoCode());
-
-                enrichData(geoData, REGION_NAME, subdivision.getName());
-                enrichData(geoData, CITY_NAME, city.getName());
-                enrichData(geoData, TIMEZONE, location.getTimeZone());
-                enrichLocationData(geoData, location.getLatitude(), location.getLongitude());
-
-                if (asn != null) {
-                    geoData.put(ASN, asn);
-                }
-
-                enrichData(geoData, ORGANIZATION_NAME, organizationName);
-                enrichData(geoData, NETWORK,network!=null? network.toString():null);
-            }
-        } catch (Exception ex) {
-            throw new EnrichFailedException("Enrichment failed exception" + ex);
-        }
-        return geoData;
-    }
-
-    /**
-     * Close the all DatabaseReader
-     */
-    @Override
-    public void closeReader() {
-        closeReaderCity();
-        closeReaderCountry();
-        closeReaderAsn();
-    }
-
-    /**
-     * Close the City DatabaseReader
-     */
-    private void closeReaderCity() {
-        try {
-            if (readerCity != null)
-                readerCity.close();
-            readerCity = null;
-        } catch (IOException ex) {
-            LOG.info("Close City DatabaseReader Exception : {0}",  ex);
-        }
-    }
-
-    /**
-     * Close the Country DatabaseReader
-     */
-    private void closeReaderCountry() {
-        try {
-            if (readerCountry != null)
-                readerCountry.close();
-            readerCountry = null;
-        } catch (IOException ex) {
-            LOG.info("Close Country DatabaseReader Exception : {0}",  ex);
-        }
-    }
-
-    /**
-     * Close the ASN DatabaseReader
-     */
-    private void closeReaderAsn() {
-        try {
-            if (readerAsn != null)
-                readerAsn.close();
-            readerAsn = null;
-        } catch (IOException ex) {
-          LOG.info("Close Asn DatabaseReader Exception : {0}",  ex);
-        }
-    }
-}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/DatabaseReaderInitializationException.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/DatabaseReaderInitializationException.java
new file mode 100644
index 0000000000..f51f249220
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/DatabaseReaderInitializationException.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+public class DatabaseReaderInitializationException extends EngineFailureException {
+    public DatabaseReaderInitializationException(final String exceptionMsg) {
+        super(exceptionMsg);
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/DownloadFailedException.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/DownloadFailedException.java
similarity index 50%
rename from data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/DownloadFailedException.java
rename to data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/DownloadFailedException.java
index b667c3273e..05cf8d9613 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/DownloadFailedException.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/DownloadFailedException.java
@@ -3,13 +3,13 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+package org.opensearch.dataprepper.plugins.processor.exception;
 
 /**
  * Implementation class for DownloadFailedException Custom exception
  */
-public class DownloadFailedException extends RuntimeException {
-    public DownloadFailedException(String exceptionMsg) {
+public class DownloadFailedException extends EngineFailureException {
+    public DownloadFailedException(final String exceptionMsg) {
         super(exceptionMsg);
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/EngineFailureException.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/EngineFailureException.java
new file mode 100644
index 0000000000..f5958b521a
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/EngineFailureException.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+public class EngineFailureException extends RuntimeException {
+    public EngineFailureException(final String exceptionMsg) {
+        super(exceptionMsg);
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/EnrichFailedException.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/EnrichFailedException.java
similarity index 67%
rename from data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/EnrichFailedException.java
rename to data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/EnrichFailedException.java
index 9182d83979..0f3dd2bfba 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/EnrichFailedException.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/EnrichFailedException.java
@@ -3,13 +3,13 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+package org.opensearch.dataprepper.plugins.processor.exception;
 
 /**
  * Implementation class for EnrichFailedException Custom exception
  */
 public class EnrichFailedException extends RuntimeException {
-    public EnrichFailedException(String exceptionMsg) {
+    public EnrichFailedException(final String exceptionMsg) {
         super(exceptionMsg);
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/InvalidIPAddressException.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/InvalidIPAddressException.java
new file mode 100644
index 0000000000..134941b5ad
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/InvalidIPAddressException.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+public class InvalidIPAddressException extends EnrichFailedException {
+    public InvalidIPAddressException(final String exceptionMsg) {
+        super(exceptionMsg);
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/NoValidDatabaseFoundException.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/NoValidDatabaseFoundException.java
new file mode 100644
index 0000000000..218d8c0901
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/exception/NoValidDatabaseFoundException.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+public class NoValidDatabaseFoundException extends EngineFailureException {
+    public NoValidDatabaseFoundException(final String exceptionMsg) {
+        super(exceptionMsg);
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplier.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplier.java
index 08e50381bf..6c07304171 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplier.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplier.java
@@ -5,15 +5,30 @@
 
 package org.opensearch.dataprepper.plugins.processor.extension;
 
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPDatabaseManager;
+
+import java.util.Optional;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
 public class DefaultGeoIpConfigSupplier implements GeoIpConfigSupplier {
     private final GeoIpServiceConfig geoIpServiceConfig;
+    private final ReentrantReadWriteLock.ReadLock readLock;
+    private final GeoIPDatabaseManager geoIPDatabaseManager;
 
-    public DefaultGeoIpConfigSupplier(final GeoIpServiceConfig geoIpServiceConfig) {
+    public DefaultGeoIpConfigSupplier(final GeoIpServiceConfig geoIpServiceConfig,
+                                      final GeoIPDatabaseManager geoIPDatabaseManager,
+                                      final ReentrantReadWriteLock.ReadLock readLock
+                                      ) {
         this.geoIpServiceConfig = geoIpServiceConfig;
+        this.geoIPDatabaseManager = geoIPDatabaseManager;
+        this.readLock = readLock;
     }
 
     @Override
-    public GeoIPProcessorService getGeoIPProcessorService() {
-        return new GeoIPProcessorService(geoIpServiceConfig);
+    public Optional<GeoIPProcessorService> getGeoIPProcessorService() {
+        if (geoIpServiceConfig != null)
+            return Optional.of(new GeoIPProcessorService(geoIpServiceConfig, geoIPDatabaseManager, readLock));
+        else
+            return Optional.empty();
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorService.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorService.java
index 7df7af684a..1ae19f0c0c 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorService.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorService.java
@@ -5,138 +5,78 @@
 
 package org.opensearch.dataprepper.plugins.processor.extension;
 
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSourceOptions;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoDataFactory;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.S3DBService;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSource;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.HttpDBDownloadService;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.LocalDBDownloadService;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.DownloadFailedException;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoData;
-import org.opensearch.dataprepper.plugins.processor.utils.DbSourceIdentification;
-import org.opensearch.dataprepper.plugins.processor.utils.LicenseTypeCheck;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPDatabaseManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
-import java.net.InetAddress;
-import java.time.Duration;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.time.Instant;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * Implementation class of geoIP-processor plugin service class.
  * It is responsible for calling of mmdb files download
  */
 public class GeoIPProcessorService {
-
     private static final Logger LOG = LoggerFactory.getLogger(GeoIPProcessorService.class);
-    public static final String DATABASE_1 = "first_database_path";
-    public static final String DATABASE_2 = "second_database_path";
-    private static final String TEMP_PATH_FOLDER = "geoip";
-    private final GeoDataFactory geoDataFactory;
-    private GetGeoData geoData;
-    private List<String> databasePaths;
-    private final String tempPath;
-    private final ScheduledExecutorService scheduledExecutorService;
-    private final DBSourceOptions dbSourceOptions;
     private final MaxMindConfig maxMindConfig;
-    private final LicenseTypeCheck licenseTypeCheck;
-    public static volatile boolean downloadReady;
-    private boolean toggle;
-    private String flipDatabase;
-    private boolean isDuringInitialization;
+    private final GeoIPDatabaseManager geoIPDatabaseManager;
+    private final ReentrantReadWriteLock.ReadLock readLock;
+    private Instant nextUpdateAt;
+    private ExecutorService executorService = null;
 
     /**
      * GeoIPProcessorService constructor for initialization of required attributes
      *
      * @param geoIpServiceConfig geoIpServiceConfig
      */
-    public GeoIPProcessorService(final GeoIpServiceConfig geoIpServiceConfig) {
-        this.toggle = false;
+    public GeoIPProcessorService(final GeoIpServiceConfig geoIpServiceConfig,
+                                 final GeoIPDatabaseManager geoIPDatabaseManager,
+                                 final ReentrantReadWriteLock.ReadLock readLock
+                                 ) {
         this.maxMindConfig = geoIpServiceConfig.getMaxMindConfig();
-        this.databasePaths = maxMindConfig.getDatabasePaths();
-        this.isDuringInitialization = true;
-        flipDatabase = DATABASE_1;
-
-        licenseTypeCheck = new LicenseTypeCheck();
-        geoDataFactory = new GeoDataFactory(maxMindConfig, licenseTypeCheck);
-        this.tempPath = System.getProperty("java.io.tmpdir") + File.separator + TEMP_PATH_FOLDER;
+        this.geoIPDatabaseManager = geoIPDatabaseManager;
+        this.readLock = readLock;
 
-        dbSourceOptions = DbSourceIdentification.getDatabasePathType(databasePaths);
-        final Duration checkInterval = Objects.requireNonNull(maxMindConfig.getDatabaseRefreshInterval());
-        scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
-        scheduledExecutorService
-                .scheduleAtFixedRate(this::downloadThroughURLandS3, 0L, checkInterval.toSeconds(), TimeUnit.SECONDS);
+        geoIPDatabaseManager.initiateDatabaseDownload();
+        this.nextUpdateAt = Instant.now().plus(maxMindConfig.getDatabaseRefreshInterval());
+    }
 
-        synchronized (this) {
-            try {
-                while (!downloadReady) {
-                    wait();
-                }
-            } catch (final InterruptedException ex) {
-                LOG.info("Thread interrupted while waiting for download to complete: {0}",  ex);
-                Thread.currentThread().interrupt();
-            }
-            if (downloadReady) {
-                geoData = geoDataFactory.create(flipDatabase);
-            }
+    public GeoIPDatabaseReader getGeoIPDatabaseReader() {
+        readLock.lock();
+        try {
+            final GeoIPDatabaseReader geoIPDatabaseReader = geoIPDatabaseManager.getGeoIPDatabaseReader();
+            geoIPDatabaseReader.retain();
+            checkAndUpdateDatabases();
+            return geoIPDatabaseReader;
+        } finally {
+            readLock.unlock();
         }
-        downloadReady = false;
     }
 
-    /**
-     * Calling download method based on the database path type
-     */
-    public synchronized void downloadThroughURLandS3() {
-        DBSource dbSource;
-        toggle = !toggle;
-        if (!toggle) {
-            flipDatabase = DATABASE_1;
-        } else {
-            flipDatabase = DATABASE_2;
+    private synchronized void checkAndUpdateDatabases() {
+        if (nextUpdateAt.isBefore(Instant.now())) {
+            nextUpdateAt = Instant.now().plus(maxMindConfig.getDatabaseRefreshInterval());
+            LOG.info("Trying to update geoip Database readers");
+            executorService = Executors.newSingleThreadExecutor();
+            executorService.execute(geoIPDatabaseManager::updateDatabaseReader);
+            executorService.shutdown();
         }
+    }
 
-        try {
-            switch (dbSourceOptions) {
-                case URL:
-                    dbSource = new HttpDBDownloadService(flipDatabase);
-                    dbSource.initiateDownload(databasePaths);
-                    downloadReady = true;
-                    break;
-                case S3:
-                    dbSource = new S3DBService(maxMindConfig.getAwsAuthenticationOptionsConfig(), flipDatabase);
-                    dbSource.initiateDownload(databasePaths);
-                    downloadReady = true;
-                    break;
-                case PATH:
-                    dbSource = new LocalDBDownloadService(flipDatabase);
-                    dbSource.initiateDownload(databasePaths);
-                    downloadReady = true;
-                    break;
-            }
-        } catch (final Exception ex) {
-            if (isDuringInitialization) {
-                throw new DownloadFailedException("Download failed due to: " + ex);
-            } else {
-                LOG.error("Download failed due to: {0}. Using previously loaded database files.", ex);
+    public void shutdown() {
+        if (executorService != null) {
+            try {
+                if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
+                    executorService.shutdownNow();
+                }
+            } catch (final InterruptedException e) {
+                executorService.shutdownNow();
             }
         }
-        isDuringInitialization = false;
-        notifyAll();
-    }
-
-    /**
-     * Method to call enrichment of data based on license type
-     * @param inetAddress inetAddress
-     * @param attributes attributes
-     * @return Enriched Map
-     */
-    public Map<String, Object> getGeoData(final InetAddress inetAddress, final List<String> attributes) {
-        return geoData.getGeoData(inetAddress, attributes, tempPath +  File.separator + flipDatabase);
+        geoIPDatabaseManager.deleteDatabasesOnShutdown();
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtension.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtension.java
index 200541339d..6f3b9d9b0d 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtension.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtension.java
@@ -9,18 +9,35 @@
 import org.opensearch.dataprepper.model.annotations.DataPrepperPluginConstructor;
 import org.opensearch.dataprepper.model.plugin.ExtensionPlugin;
 import org.opensearch.dataprepper.model.plugin.ExtensionPoints;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPFileManager;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderBuilder;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPDatabaseManager;
+import org.opensearch.dataprepper.plugins.processor.utils.LicenseTypeCheck;
 
-@DataPrepperExtensionPlugin(modelType = GeoIpServiceConfig.class, rootKeyJsonPath = "/geoip_service")
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+@DataPrepperExtensionPlugin(modelType = GeoIpServiceConfig.class, rootKeyJsonPath = "/geoip_service", allowInPipelineConfigurations = true)
 public class GeoIpConfigExtension implements ExtensionPlugin {
     private final DefaultGeoIpConfigSupplier defaultGeoIpConfigSupplier;
 
     @DataPrepperPluginConstructor
     public GeoIpConfigExtension(final GeoIpServiceConfig geoIpServiceConfig) {
-        this.defaultGeoIpConfigSupplier = new DefaultGeoIpConfigSupplier(geoIpServiceConfig != null ? geoIpServiceConfig : new GeoIpServiceConfig());
+            final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(true);
+            GeoIPDatabaseManager geoIPDatabaseManager = null;
+            if (geoIpServiceConfig != null) {
+                geoIPDatabaseManager = new GeoIPDatabaseManager(
+                        geoIpServiceConfig.getMaxMindConfig(),
+                        new LicenseTypeCheck(),
+                        new DatabaseReaderBuilder(),
+                        new GeoIPFileManager(),
+                        reentrantReadWriteLock.writeLock()
+                );
+            }
+            this.defaultGeoIpConfigSupplier = new DefaultGeoIpConfigSupplier(geoIpServiceConfig, geoIPDatabaseManager, reentrantReadWriteLock.readLock());
     }
 
     @Override
     public void apply(final ExtensionPoints extensionPoints) {
-        extensionPoints.addExtensionProvider(new GeoIpConfigProvider(this.defaultGeoIpConfigSupplier));
+            extensionPoints.addExtensionProvider(new GeoIpConfigProvider(this.defaultGeoIpConfigSupplier));
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigSupplier.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigSupplier.java
index a754c9a745..8e00ef6f3e 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigSupplier.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigSupplier.java
@@ -5,6 +5,8 @@
 
 package org.opensearch.dataprepper.plugins.processor.extension;
 
+import java.util.Optional;
+
 /**
  * Interface for supplying {@link GeoIPProcessorService} to {@link GeoIpConfigExtension}
  *
@@ -16,5 +18,5 @@ public interface GeoIpConfigSupplier {
      *
      * @since 2.7
      */
-    GeoIPProcessorService getGeoIPProcessorService();
+    Optional<GeoIPProcessorService> getGeoIPProcessorService();
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpServiceConfig.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpServiceConfig.java
index c311f773ca..3dc974e698 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpServiceConfig.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpServiceConfig.java
@@ -11,12 +11,8 @@
 public class GeoIpServiceConfig {
     private static final MaxMindConfig DEFAULT_MAXMIND_CONFIG = new MaxMindConfig();
 
-    public GeoIpServiceConfig() {
-        // This default constructor is used if geoip_service is not configured
-    }
-
-    @JsonProperty("maxmind")
     @Valid
+    @JsonProperty("maxmind")
     private MaxMindConfig maxMindConfig = DEFAULT_MAXMIND_CONFIG;
 
     /**
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfig.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfig.java
index 3fea586e67..b03ec864d4 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfig.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfig.java
@@ -11,12 +11,17 @@
 import jakarta.validation.constraints.Min;
 import org.hibernate.validator.constraints.time.DurationMax;
 import org.hibernate.validator.constraints.time.DurationMin;
+import org.opensearch.dataprepper.plugins.processor.utils.DatabaseSourceIdentification;
 
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.List;
 
 public class MaxMindConfig {
+    private static final boolean DEFAULT_INSECURE = false;
     private static final String S3_PREFIX = "s3://";
 
     //TODO: Add validations to database paths
@@ -24,6 +29,7 @@ public class MaxMindConfig {
     private static final List<String> DEFAULT_DATABASE_PATHS = new ArrayList<>();
     private static final Duration DEFAULT_DATABASE_REFRESH_INTERVAL = Duration.ofDays(7);
     private static final int DEFAULT_CACHE_SIZE = 4096;
+    private static final String DEFAULT_DATABASE_DESTINATION = System.getProperty("data-prepper.dir") + File.separator + "data";
 
     @JsonProperty("database_paths")
     private List<String> databasePaths = DEFAULT_DATABASE_PATHS;
@@ -38,17 +44,22 @@ public class MaxMindConfig {
     //TODO:  Add a Max limit on cache size
     private int cacheSize = DEFAULT_CACHE_SIZE;
 
-    //TODO: Add a destination path to store database files
-    @JsonProperty("aws")
     @Valid
+    @JsonProperty("aws")
     private AwsAuthenticationOptionsConfig awsAuthenticationOptionsConfig;
 
+    @JsonProperty("insecure")
+    private boolean insecure = DEFAULT_INSECURE;
+
+    @JsonProperty("database_destination_path")
+    private String databaseDestination = DEFAULT_DATABASE_DESTINATION;
+
     public MaxMindConfig() {
         // This default constructor is used if maxmind is not configured
     }
 
     @AssertTrue(message = "aws should be configured if any path in database_paths is S3 bucket path.")
-    boolean isAwsAuthenticationOptionsRequired() {
+    public boolean isAwsAuthenticationOptionsRequired() {
         for (final String databasePath : databasePaths) {
             if (databasePath.startsWith(S3_PREFIX)) {
                 return awsAuthenticationOptionsConfig != null;
@@ -57,6 +68,19 @@ boolean isAwsAuthenticationOptionsRequired() {
         return true;
     }
 
+    @AssertTrue(message = "database_paths should be https endpoint if using URL and if insecure is set to false")
+    public boolean isHttpsEndpointOrInsecure() throws URISyntaxException {
+        if (insecure) {
+            return true;
+        }
+        for (final String databasePath : databasePaths) {
+            if (DatabaseSourceIdentification.isURL(databasePath)) {
+                return new URI(databasePath).getScheme().equals("https");
+            }
+        }
+        return true;
+    }
+
     /**
      * Gets the MaxMind database paths
      *
@@ -96,4 +120,14 @@ public int getCacheSize() {
     public AwsAuthenticationOptionsConfig getAwsAuthenticationOptionsConfig() {
         return awsAuthenticationOptionsConfig;
     }
+
+    /**
+     * Gets the destination folder to store database files
+     *
+     * @return The destination folder
+     * @since 2.7
+     */
+    public String getDatabaseDestination() {
+        return databaseDestination + File.separator + "geoip";
+    }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSource.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSource.java
index 3ead66744c..8c30dd80a7 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSource.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSource.java
@@ -5,17 +5,12 @@
 
 package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.X509TrustManager;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.HostnameVerifier;
-import java.io.File;
-import java.io.UncheckedIOException;
 import java.security.KeyManagementException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -24,47 +19,7 @@
 import java.util.List;
 
 public interface DBSource {
-
-    public static final Logger LOG = LoggerFactory.getLogger(DBSource.class);
-    public String tempFolderPath = System.getProperty("java.io.tmpdir")+ File.separator +"GeoIP";
-    public String tarFolderPath = tempFolderPath + "/tar";
-    public String downloadTarFilepath = tarFolderPath + "/out.tar.gz";
-    public void initiateDownload(List<String> config) throws Exception;
-
-    /**
-     * create Folder If Not Exist
-     * @param outputFilePath Output File Path
-     * @return File
-     */
-    static File createFolderIfNotExist(String outputFilePath) {
-        final File destFile = new File(outputFilePath);
-        try {
-            if (!destFile.exists()) {
-                destFile.mkdirs();
-            }
-        }
-        catch (UncheckedIOException ex) {
-            LOG.info("Create Folder If NotExist Exception {0}", ex);
-        }
-        return destFile;
-    }
-
-    /**
-     * Delete Directory
-     * @param file file
-     */
-    static void deleteDirectory(File file) {
-
-        if (file.exists()) {
-            for (final File subFile : file.listFiles()) {
-                if (subFile.isDirectory()) {
-                    deleteDirectory(subFile);
-                }
-                subFile.delete();
-            }
-            file.delete();
-        }
-    }
+    void initiateDownload(List<String> config) throws Exception;
 
     /**
      * initiateSSL
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceOptions.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceOptions.java
index 485274dd9d..60eacff72a 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceOptions.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceOptions.java
@@ -17,7 +17,8 @@
 public enum DBSourceOptions {
     PATH("path"),
     URL("url"),
-    S3("s3");
+    S3("s3"),
+    HTTP_MANIFEST("http_manifest");
 
     private final String option;
 
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderCreate.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderBuilder.java
similarity index 80%
rename from data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderCreate.java
rename to data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderBuilder.java
index 47546c6c9f..841b4c7fbc 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderCreate.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderBuilder.java
@@ -15,7 +15,9 @@
 /**
  * Implementation class for DatabaseReader Creation
  */
-public class DatabaseReaderCreate {
+public class DatabaseReaderBuilder {
+    public DatabaseReaderBuilder() {
+    }
 
     /**
      * Creates DatabaseReader instance based on in memory or cache type
@@ -23,7 +25,7 @@ public class DatabaseReaderCreate {
      * @param cacheSize cacheSize
      * @return DatabaseReader
      */
-    public static DatabaseReader createLoader(final Path databasePath, final int cacheSize) throws IOException {
+    public DatabaseReader buildReader(final Path databasePath, final int cacheSize) throws IOException {
         return new DatabaseReader.Builder(databasePath.toFile())
                 .fileMode(Reader.FileMode.MEMORY_MAPPED)
                 .withCache(new CHMCache(cacheSize))
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoDataFactory.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoDataFactory.java
deleted file mode 100644
index e05453d37f..0000000000
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoDataFactory.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
-
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoData;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoIP2Data;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoLite2Data;
-import org.opensearch.dataprepper.plugins.processor.extension.MaxMindConfig;
-import org.opensearch.dataprepper.plugins.processor.utils.LicenseTypeCheck;
-
-import java.io.File;
-
-import static org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSource.tempFolderPath;
-
-public class GeoDataFactory {
-    private final MaxMindConfig maxMindConfig;
-    private final LicenseTypeCheck licenseTypeCheck;
-
-    public GeoDataFactory(final MaxMindConfig maxMindConfig, final LicenseTypeCheck licenseTypeCheck) {
-        this.maxMindConfig = maxMindConfig;
-        this.licenseTypeCheck = licenseTypeCheck;
-    }
-
-    /**
-     * Creates GetGeoData class based on LicenseTypeOptions
-     */
-    public GetGeoData create(final String databasePath) {
-        final String finalPath = tempFolderPath + File.separator + databasePath;
-        final LicenseTypeOptions licenseType = licenseTypeCheck.isGeoLite2OrEnterpriseLicense(finalPath);
-        if (licenseType.equals(LicenseTypeOptions.FREE)) {
-            return new GetGeoLite2Data(finalPath, maxMindConfig.getCacheSize());
-        } else {
-            return new GetGeoIP2Data(finalPath, maxMindConfig.getCacheSize());
-        }
-    }
-}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPDatabaseManager.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPDatabaseManager.java
new file mode 100644
index 0000000000..4efd3cbdd2
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPDatabaseManager.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIP2DatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoLite2DatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.exception.DownloadFailedException;
+import org.opensearch.dataprepper.plugins.processor.exception.NoValidDatabaseFoundException;
+import org.opensearch.dataprepper.plugins.processor.extension.MaxMindConfig;
+import org.opensearch.dataprepper.plugins.processor.utils.DatabaseSourceIdentification;
+import org.opensearch.dataprepper.plugins.processor.utils.LicenseTypeCheck;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+public class GeoIPDatabaseManager {
+    private static final Logger LOG = LoggerFactory.getLogger(GeoIPDatabaseManager.class);
+    public static final String FIRST_DATABASE_DIR = "first_database";
+    public static final String SECOND_DATABASE_DIR = "second_database";
+    private final MaxMindConfig maxMindConfig;
+    private final LicenseTypeCheck licenseTypeCheck;
+    private final DatabaseReaderBuilder databaseReaderBuilder;
+    private final List<String> databasePaths;
+    private final WriteLock writeLock;
+    private final int cacheSize;
+    private final GeoIPFileManager geoIPFileManager;
+    private DBSourceOptions dbSourceOptions;
+    private String currentDatabaseDir;
+    private GeoIPDatabaseReader geoIPDatabaseReader;
+    private boolean databaseDirToggle;
+    private boolean downloadReady;
+
+    public GeoIPDatabaseManager(final MaxMindConfig maxMindConfig,
+                                final LicenseTypeCheck licenseTypeCheck,
+                                final DatabaseReaderBuilder databaseReaderBuilder,
+                                final GeoIPFileManager geoIPFileManager,
+                                final ReentrantReadWriteLock.WriteLock writeLock
+                                ) {
+        this.maxMindConfig = maxMindConfig;
+        this.licenseTypeCheck = licenseTypeCheck;
+        this.databaseReaderBuilder = databaseReaderBuilder;
+        this.geoIPFileManager = geoIPFileManager;
+        this.databasePaths = maxMindConfig.getDatabasePaths();
+        this.writeLock = writeLock;
+        this.cacheSize = maxMindConfig.getCacheSize();
+    }
+
+    public void initiateDatabaseDownload() {
+        dbSourceOptions = DatabaseSourceIdentification.getDatabasePathType(databasePaths);
+        try {
+            downloadDatabases();
+        } catch (final Exception e) {
+            throw new DownloadFailedException(e.getMessage());
+        }
+        synchronized (this) {
+            try {
+                while (!downloadReady) {
+                    wait();
+                }
+            } catch (final InterruptedException ex) {
+                LOG.info("Thread interrupted while waiting for download to complete: {0}",  ex);
+                Thread.currentThread().interrupt();
+            }
+            if (downloadReady) {
+                try {
+                    geoIPDatabaseReader = createReader();
+                } catch (final NoValidDatabaseFoundException e) {
+                    throw new NoValidDatabaseFoundException(e.getMessage());
+                }
+            }
+        }
+        downloadReady = false;
+    }
+
+    public void updateDatabaseReader() {
+        try {
+            downloadDatabases();
+        } catch (final Exception e) {
+            LOG.error("Database download failed, using previously loaded database. {}", e.getMessage());
+            final File file = new File(currentDatabaseDir);
+            geoIPFileManager.deleteDirectory(file);
+            switchDirectory();
+        }
+
+        if (downloadReady) {
+            downloadReady = false;
+            try {
+                switchDatabase();
+            } catch (final Exception e) {
+                LOG.error("Failed to update databases. Please make sure the database files exist at configured path amd are valid. " +
+                        "Using previously loaded database. {}", e.getMessage());
+                final File file = new File(currentDatabaseDir);
+                geoIPFileManager.deleteDirectory(file);
+                switchDirectory();
+            }
+        }
+    }
+
+    private void switchDatabase() {
+        writeLock.lock();
+        try {
+            final GeoIPDatabaseReader newGeoipDatabaseReader = createReader();
+            final GeoIPDatabaseReader oldGeoipDatabaseReader = geoIPDatabaseReader;
+            geoIPDatabaseReader = newGeoipDatabaseReader;
+            oldGeoipDatabaseReader.close();
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    private void downloadDatabases() throws Exception {
+        DBSource dbSource;
+        switchDirectory();
+
+        final String destinationPath = maxMindConfig.getDatabaseDestination() + File.separator + currentDatabaseDir;
+        geoIPFileManager.createDirectoryIfNotExist(destinationPath);
+        switch (dbSourceOptions) {
+            case HTTP_MANIFEST:
+                dbSource = new ManifestDownloadService(destinationPath);
+                dbSource.initiateDownload(databasePaths);
+                downloadReady =true;
+                break;
+            case URL:
+                dbSource = new HttpDBDownloadService(destinationPath, geoIPFileManager);
+                dbSource.initiateDownload(databasePaths);
+                downloadReady = true;
+                break;
+            case S3:
+                dbSource = new S3DBService(maxMindConfig.getAwsAuthenticationOptionsConfig(), destinationPath);
+                dbSource.initiateDownload(databasePaths);
+                downloadReady = true;
+                break;
+            case PATH:
+                dbSource = new LocalDBDownloadService(destinationPath);
+                dbSource.initiateDownload(databasePaths);
+                downloadReady = true;
+                break;
+        }
+    }
+
+    private GeoIPDatabaseReader createReader() {
+        final String finalPath = maxMindConfig.getDatabaseDestination() + File.separator + currentDatabaseDir;
+        final LicenseTypeOptions licenseType = licenseTypeCheck.isGeoLite2OrEnterpriseLicense(finalPath);
+        if (licenseType == null) {
+            throw new NoValidDatabaseFoundException("At least one valid database is required.");
+        }
+        GeoIPDatabaseReader newGeoIPDatabaseReader;
+        if (licenseType.equals(LicenseTypeOptions.FREE)) {
+            newGeoIPDatabaseReader = new GeoLite2DatabaseReader(databaseReaderBuilder, geoIPFileManager, finalPath, cacheSize);
+        } else {
+            newGeoIPDatabaseReader = new GeoIP2DatabaseReader(databaseReaderBuilder, geoIPFileManager, finalPath, cacheSize);
+        }
+        return newGeoIPDatabaseReader;
+    }
+
+    private void switchDirectory() {
+        databaseDirToggle = !databaseDirToggle;
+        if (databaseDirToggle) {
+            currentDatabaseDir = FIRST_DATABASE_DIR;
+        } else {
+            currentDatabaseDir = SECOND_DATABASE_DIR;
+        }
+    }
+
+    public GeoIPDatabaseReader getGeoIPDatabaseReader() {
+        return geoIPDatabaseReader;
+    }
+
+    public void deleteDatabasesOnShutdown() {
+        geoIPFileManager.deleteDirectory(new File(maxMindConfig.getDatabaseDestination() + File.separator + FIRST_DATABASE_DIR));
+        geoIPFileManager.deleteDirectory(new File(maxMindConfig.getDatabaseDestination() + File.separator + SECOND_DATABASE_DIR));
+    }
+
+    public void deleteDirectory(final File file) {
+
+        if (file.exists()) {
+            for (final File subFile : file.listFiles()) {
+                if (subFile.isDirectory()) {
+                    deleteDirectory(subFile);
+                }
+                subFile.delete();
+            }
+            file.delete();
+        }
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPFileManager.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPFileManager.java
new file mode 100644
index 0000000000..66dda2db70
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPFileManager.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import java.io.File;
+
+public class GeoIPFileManager {
+    public void deleteDirectory(final File file) {
+
+        if (file.exists()) {
+            for (final File subFile : file.listFiles()) {
+                if (subFile.isDirectory()) {
+                    deleteDirectory(subFile);
+                }
+                subFile.delete();
+            }
+            file.delete();
+        }
+    }
+
+    public void deleteFile(final File file) {
+        file.delete();
+    }
+
+    public void createDirectoryIfNotExist(final String outputFilePath) {
+        final File destFile = new File(outputFilePath);
+        if (!destFile.exists()) {
+            destFile.mkdirs();
+        }
+    }
+
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadService.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadService.java
index d407f9bdb7..d28f6ab67e 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadService.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadService.java
@@ -26,15 +26,17 @@
 public class HttpDBDownloadService implements DBSource {
 
     private static final Logger LOG = LoggerFactory.getLogger(HttpDBDownloadService.class);
-    private final String prefixDir;
+    private final String destinationDirectory;
     private static final int DEFAULT_BYTE_SIZE = 1024;
+    private final GeoIPFileManager geoIPFileManager;
 
     /**
      * HttpDBDownloadService constructor for initialisation of attributes
-     * @param prefixDir prefixDir
+     * @param destinationDirectory destinationDirectory
      */
-    public HttpDBDownloadService(String prefixDir) {
-        this.prefixDir = prefixDir;
+    public HttpDBDownloadService(final String destinationDirectory, final GeoIPFileManager geoIPFileManager) {
+        this.destinationDirectory = destinationDirectory;
+        this.geoIPFileManager = geoIPFileManager;
     }
 
     /**
@@ -42,14 +44,15 @@ public HttpDBDownloadService(String prefixDir) {
      * @param urlList urlList
      */
     public void initiateDownload(List<String> urlList) {
-        final File tmpDir = DBSource.createFolderIfNotExist(tempFolderPath + File.separator + prefixDir);
-        for(String url : urlList) {
-            DBSource.createFolderIfNotExist(tarFolderPath);
+        final String tarDir = destinationDirectory + File.separator + "tar";
+        final String downloadTarFilepath = tarDir + File.separator + "out.tar.gz";
+        for(final String url : urlList) {
+            geoIPFileManager.createDirectoryIfNotExist(tarDir);
             try {
                 initiateSSL();
-                buildRequestAndDownloadFile(url);
-                decompressAndUntarFile(tarFolderPath, downloadTarFilepath, tmpDir);
-                deleteTarFolder(tarFolderPath);
+                buildRequestAndDownloadFile(url, downloadTarFilepath);
+                decompressAndUntarFile(tarDir, downloadTarFilepath, new File(destinationDirectory));
+                deleteTarFolder(tarDir);
             } catch (Exception ex) {
                 LOG.info("InitiateDownload Exception {0} " , ex);
             }
@@ -62,7 +65,7 @@ public void initiateDownload(List<String> urlList) {
      * @param downloadTarFilepath downloadTarFilepath
      * @param tmpDir tmpDir
      */
-    private void decompressAndUntarFile(String tarFolderPath, String downloadTarFilepath, File tmpDir) {
+    private void decompressAndUntarFile(final String tarFolderPath, final String downloadTarFilepath, final File tmpDir) {
         try {
             final File inputFile = new File(downloadTarFilepath);
             final String outputFile = getFileName(inputFile, tarFolderPath);
@@ -80,17 +83,17 @@ private void decompressAndUntarFile(String tarFolderPath, String downloadTarFile
      * Build Request And DownloadFile
      * @param url url
      */
-    public void buildRequestAndDownloadFile(String... url)  {
-        downloadDBFileFromMaxmind(url[0], downloadTarFilepath);
+    public void buildRequestAndDownloadFile(final String url, final String downloadTarFilepath)  {
+        downloadDBFileFromMaxmind(url, downloadTarFilepath);
     }
 
     /**
      * Delete Tar Folder
      * @param tarFolder Tar Folder
      */
-    private static void deleteTarFolder(String tarFolder) {
+    private void deleteTarFolder(String tarFolder) {
         final File file = new File(tarFolder);
-        DBSource.deleteDirectory(file);
+        geoIPFileManager.deleteDirectory(file);
         if (file.exists()) {
             file.delete();
         }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/LocalDBDownloadService.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/LocalDBDownloadService.java
index d4a85218fa..ae55c2343c 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/LocalDBDownloadService.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/LocalDBDownloadService.java
@@ -15,14 +15,14 @@
  */
 public class LocalDBDownloadService implements DBSource {
 
-    private final String prefixDir;
+    private final String destinationDirectory;
 
     /**
      * LocalDBDownloadService constructor for initialisation of attributes
-     * @param prefixDir prefixDir
+     * @param destinationDirectory destinationDirectory
      */
-    public LocalDBDownloadService(final String prefixDir) {
-        this.prefixDir = prefixDir;
+    public LocalDBDownloadService(final String destinationDirectory) {
+        this.destinationDirectory = destinationDirectory;
     }
 
     /**
@@ -31,10 +31,8 @@ public LocalDBDownloadService(final String prefixDir) {
      */
     @Override
     public void initiateDownload(List<String> config) throws Exception {
-        String destPath = tempFolderPath + File.separator + prefixDir;
-        DBSource.createFolderIfNotExist(destPath);
         File srcDatabaseConfigPath = new File(config.get(0));
-        File destDatabaseConfigPath = new File(destPath);
+        File destDatabaseConfigPath = new File(destinationDirectory);
         FileUtils.copyDirectory(srcDatabaseConfigPath, destDatabaseConfigPath);
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/Manifest.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/Manifest.java
new file mode 100644
index 0000000000..f29bd3c2fb
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/Manifest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Manifest {
+    @JsonProperty("url")
+    private String url;
+    @JsonProperty("db_name")
+    private String dbName;
+    @JsonProperty("sha256_hash")
+    private String sha256Hash;
+    @JsonProperty("valid_for_in_days")
+    private int validForInDays;
+    @JsonProperty("updated_at_in_epoch_milli")
+    private long updatedAt;
+    @JsonProperty("provider")
+    private String provider;
+
+    public String getUrl() {
+        return url;
+    }
+
+    public String getDbName() {
+        return dbName;
+    }
+
+    public String getSha256Hash() {
+        return sha256Hash;
+    }
+
+    public int getValidForInDays() {
+        return validForInDays;
+    }
+
+    public Long getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public String getProvider() {
+        return provider;
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/ManifestDownloadService.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/ManifestDownloadService.java
new file mode 100644
index 0000000000..6cc12acee6
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/ManifestDownloadService.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.opensearch.dataprepper.plugins.processor.exception.DownloadFailedException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import static org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader.MAXMIND_DATABASE_EXTENSION;
+
+
+public class ManifestDownloadService implements DBSource {
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    private static final int DEFAULT_BYTE_SIZE = 1024;
+    private static final String CITY_DATABASE_NAME = "geolite2-city.mmdb";
+    private static final String COUNTRY_DATABASE_NAME = "geolite2-country.mmdb";
+    private static final String ASN_DATABASE_NAME = "geolite2-asn.mmdb";
+    private static final String ZIP_FILE_EXTENSION = ".zip";
+    private final String directoryName;
+
+    private Manifest cityManifest;
+    private Manifest countryManifest;
+    private Manifest asnManifest;
+
+    public ManifestDownloadService(final String directoryName) {
+        this.directoryName = directoryName;
+    }
+
+    @Override
+    public void initiateDownload(final List<String> databasePaths) {
+        for(final String url : databasePaths) {
+            final Manifest manifest = deserializeManifestFile(url);
+            populateManifestObjects(manifest);
+
+            final String manifestFilePath = manifest.getDbName();
+            final String zipFileName = manifestFilePath.substring(0, manifestFilePath.lastIndexOf(".")).concat(ZIP_FILE_EXTENSION);
+            final String zipFilePath = directoryName + File.separator + zipFileName;
+
+            downloadZipFile(manifest.getUrl(), zipFilePath);
+            unzipDownloadedFile(zipFilePath, directoryName);
+        }
+    }
+
+    private Manifest deserializeManifestFile(final String CDNEndpoint) {
+        HttpURLConnection httpURLConnection = null;
+        try {
+            final URL url = new URL(CDNEndpoint);
+            httpURLConnection = (HttpURLConnection) url.openConnection();
+            httpURLConnection.addRequestProperty("User-Agent", "Custom-User-Agent");
+
+            final Manifest manifest = OBJECT_MAPPER.readValue(httpURLConnection.getInputStream(), Manifest.class);
+            httpURLConnection.disconnect();
+
+            return manifest;
+        } catch (final IOException ex) {
+            if (httpURLConnection != null) {
+                httpURLConnection.disconnect();
+            }
+            throw new DownloadFailedException("Exception occurred while reading manifest.json file due to: " + ex);
+        }
+    }
+
+    private void populateManifestObjects(final Manifest manifest) {
+        switch (manifest.getDbName()) {
+            case CITY_DATABASE_NAME:
+                cityManifest = manifest;
+                break;
+            case COUNTRY_DATABASE_NAME:
+                countryManifest = manifest;
+                break;
+            case ASN_DATABASE_NAME:
+                asnManifest = manifest;
+                break;
+        }
+    }
+
+    private void downloadZipFile(final String databaseUrl, final String destinationPath) {
+        HttpURLConnection httpURLConnection;
+        try {
+            final URL url = new URL(databaseUrl);
+            httpURLConnection = (HttpURLConnection) url.openConnection();
+            // CDN endpoint returns 403 without User Agent.
+            httpURLConnection.addRequestProperty("User-Agent", "Data Prepper");
+        } catch (IOException ex) {
+            throw new DownloadFailedException("Exception occurred while opening connection due to: " + ex.getMessage());
+        }
+
+        try (final BufferedInputStream in = new BufferedInputStream(httpURLConnection.getInputStream());
+             final FileOutputStream fileOutputStream = new FileOutputStream(destinationPath)) {
+            final byte[] dataBuffer = new byte[DEFAULT_BYTE_SIZE];
+            int bytesRead;
+            while ((bytesRead = in.read(dataBuffer, 0, DEFAULT_BYTE_SIZE)) != -1) {
+                fileOutputStream.write(dataBuffer, 0, bytesRead);
+            }
+            httpURLConnection.disconnect();
+        } catch (final IOException ex) {
+            httpURLConnection.disconnect();
+            throw new DownloadFailedException("Exception occurred while downloading MaxMind database due to: " + ex.getMessage());
+        }
+    }
+
+    private void unzipDownloadedFile(final String zipFilePath, final String outputFilePath) {
+        final File inputFile = new File(zipFilePath);
+        final File outputDir = new File(outputFilePath);
+        if (!outputDir.exists()) {
+            outputDir.mkdirs();
+        }
+
+        final byte[] buffer = new byte[DEFAULT_BYTE_SIZE];
+
+        try (final FileInputStream fileInputStream = new FileInputStream(inputFile);
+             final ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
+
+            ZipEntry zipEntry = zipInputStream.getNextEntry();
+            while (zipEntry != null && zipEntry.getName().endsWith(MAXMIND_DATABASE_EXTENSION)) {
+                final String fileName = zipEntry.getName();
+                final File newFile = new File(outputDir + File.separator + fileName);
+
+                final FileOutputStream fileOutputStream = new FileOutputStream(newFile);
+                int len;
+                while ((len = zipInputStream.read(buffer)) > 0) {
+                    fileOutputStream.write(buffer, 0, len);
+                }
+                fileOutputStream.close();
+                zipInputStream.closeEntry();
+                zipEntry = zipInputStream.getNextEntry();
+            }
+
+            // deleting zip file after unzipping
+            inputFile.delete();
+        } catch (final IOException e) {
+            inputFile.delete();
+            throw new DownloadFailedException("Exception occurred while unzipping the database file due to: " + e.getMessage());
+        }
+    }
+
+
+    public Manifest getCityManifest() {
+        return cityManifest;
+    }
+
+    public Manifest getCountryManifest() {
+        return countryManifest;
+    }
+
+    public Manifest getAsnManifest() {
+        return asnManifest;
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBService.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBService.java
index 11793a8189..144facd034 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBService.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBService.java
@@ -5,7 +5,7 @@
 
 package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
 
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.DownloadFailedException;
+import org.opensearch.dataprepper.plugins.processor.exception.DownloadFailedException;
 import org.opensearch.dataprepper.plugins.processor.extension.AwsAuthenticationOptionsConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -14,7 +14,6 @@
 import software.amazon.awssdk.transfer.s3.model.DirectoryDownload;
 import software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest;
 
-import java.io.File;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.file.Paths;
@@ -29,30 +28,29 @@ public class S3DBService implements DBSource {
     private String bucketName;
     private String bucketPath;
     private final AwsAuthenticationOptionsConfig awsAuthenticationOptionsConfig;
-    private final String prefixDir;
+    private final String destinationDirectory;
 
     /**
      * S3DBService constructor for initialisation of attributes
      *
-     * @param prefixDir prefixDir
+     * @param destinationDirectory destinationDirectory
      */
     public S3DBService(final AwsAuthenticationOptionsConfig awsAuthenticationOptionsConfig,
-                       final String prefixDir) {
+                       final String destinationDirectory) {
         this.awsAuthenticationOptionsConfig = awsAuthenticationOptionsConfig;
-        this.prefixDir = prefixDir;
+        this.destinationDirectory = destinationDirectory;
     }
 
     /**
      * Initialisation of Download through Url
      * @param s3URLs s3URLs
      */
-    public void initiateDownload(List<String> s3URLs) {
+    public void initiateDownload(final List<String> s3URLs) {
         for (String s3Url : s3URLs) {
             try {
                 URI uri = new URI(s3Url);
                 bucketName = uri.getHost();
                 bucketPath = removeTrailingSlash(removeLeadingSlash(uri.getPath()));
-                DBSource.createFolderIfNotExist(tempFolderPath + File.separator + prefixDir);
                 buildRequestAndDownloadFile(bucketName, bucketPath);
             } catch (URISyntaxException ex) {
                 LOG.info("Initiate Download Exception", ex);
@@ -96,7 +94,7 @@ public void buildRequestAndDownloadFile(String... path) {
             DirectoryDownload directoryDownload =
                     transferManager.downloadDirectory(
                             DownloadDirectoryRequest.builder()
-                                    .destination(Paths.get(tempFolderPath + File.separator + prefixDir))
+                                    .destination(Paths.get(destinationDirectory))
                                     .bucket(path[0])
                                     .listObjectsV2RequestTransformer(l -> l.prefix(path[1]))
                                     .build());
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentification.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/DatabaseSourceIdentification.java
similarity index 52%
rename from data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentification.java
rename to data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/DatabaseSourceIdentification.java
index e7eaffe75d..1afa8fda88 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentification.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/DatabaseSourceIdentification.java
@@ -7,6 +7,7 @@
 
 import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSourceOptions;
 
+import java.io.File;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -17,20 +18,21 @@
 /**
  * Implementation of class for checking whether URL type is S3 or file path
  */
-public class DbSourceIdentification {
+public class DatabaseSourceIdentification {
 
-    private DbSourceIdentification() {
+    private DatabaseSourceIdentification() {
 
     }
 
-    private static String s3DomainPattern = "[a-zA-Z0-9-]+\\.s3\\.amazonaws\\.com";
+    private static final String S3_DOMAIN_PATTERN = "[a-zA-Z0-9-]+\\.s3\\.amazonaws\\.com";
+    private static final String MANIFEST_ENDPOINT_PATH = "manifest.json";
 
     /**
      * Check for database path is valid S3 URI or not
      * @param uriString uriString
      * @return boolean
      */
-    public static boolean isS3Uri(String uriString) {
+    public static boolean isS3Uri(final String uriString) {
         try {
             URI uri = new URI(uriString);
             if (uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("s3")) {
@@ -43,44 +45,48 @@ public static boolean isS3Uri(String uriString) {
     }
 
     /**
-     * Check for database path is valid S3 URL or not
-     * @param urlString urlString
+     * Check for database path is valid URL or not
+     * @param input input
      * @return boolean
      */
-    public static boolean isS3Url(String urlString) {
+    public static boolean isURL(final String input) {
         try {
-            URL url = new URL(urlString);
-            if (Pattern.matches(s3DomainPattern, url.getHost())) {
-                return true;
-            }
-        } catch (Exception e) {
+            final URI uri = new URI(input);
+            final URL url = new URL(input);
+            return !input.endsWith(MANIFEST_ENDPOINT_PATH) &&
+                    uri.getScheme() != null &&
+                    !Pattern.matches(S3_DOMAIN_PATTERN, url.getHost()) &&
+                    (uri.getScheme().equals("http") || uri.getScheme().equals("https"));
+        } catch (URISyntaxException | MalformedURLException e) {
             return false;
         }
-        return false;
     }
 
     /**
-     * Check for database path is valid URL or not
+     * Check for database path is local file path or not
      * @param input input
      * @return boolean
      */
-    public static boolean isURL(String input) {
-        try {
-            URI uri = new URI(input);
-            URL url = new URL(input);
-            return uri.getScheme() != null && !Pattern.matches(s3DomainPattern, url.getHost()) &&(uri.getScheme().equals("http") || uri.getScheme().equals("https"));
-        } catch (URISyntaxException | MalformedURLException e) {
-            return false;
-        }
+    public static boolean isFilePath(final String input) {
+        final File file = new File(input);
+        return file.exists() && file.isDirectory();
     }
 
     /**
-     * Check for database path is local file path or not
+     * Check for database path is CDN endpoint
      * @param input input
      * @return boolean
      */
-    public static boolean isFilePath(String input) {
-        return input.startsWith("/") || input.startsWith("./") || input.startsWith("\\") || (input.length() > 1 && input.charAt(1) == ':');
+    public static boolean isCDNEndpoint(final String input) {
+        if (input.endsWith(MANIFEST_ENDPOINT_PATH)) {
+            try {
+                final URI uri = new URI(input);
+                return uri.getScheme().equals("http") || uri.getScheme().equals("https");
+            } catch (final URISyntaxException e) {
+                return false;
+            }
+        }
+        return false;
     }
 
     /**
@@ -88,18 +94,21 @@ public static boolean isFilePath(String input) {
      * @param databasePaths - List of database paths to get databases data from
      * @return DBSourceOptions
      */
-    public static DBSourceOptions getDatabasePathType(List<String> databasePaths) {
+    public static DBSourceOptions getDatabasePathType(final List<String> databasePaths) {
         DBSourceOptions downloadSourceOptions = null;
         for(final String databasePath : databasePaths) {
 
-            if(DbSourceIdentification.isFilePath(databasePath)) {
+            if(DatabaseSourceIdentification.isFilePath(databasePath)) {
                 return DBSourceOptions.PATH;
             }
-            else if(DbSourceIdentification.isURL(databasePath))
+            else if (DatabaseSourceIdentification.isCDNEndpoint(databasePath)) {
+                downloadSourceOptions = DBSourceOptions.HTTP_MANIFEST;
+            }
+            else if(DatabaseSourceIdentification.isURL(databasePath))
             {
                 downloadSourceOptions = DBSourceOptions.URL;
             }
-            else if(DbSourceIdentification.isS3Uri(databasePath) || (DbSourceIdentification.isS3Url(databasePath)))
+            else if(DatabaseSourceIdentification.isS3Uri(databasePath))
             {
                 downloadSourceOptions = DBSourceOptions.S3;
             }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheck.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheck.java
index d717ebc711..dceb1bc591 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheck.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheck.java
@@ -5,6 +5,8 @@
 
 package org.opensearch.dataprepper.plugins.processor.utils;
 
+import org.opensearch.dataprepper.plugins.processor.exception.InvalidIPAddressException;
+
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -20,10 +22,15 @@ public class IPValidationCheck {
      * Check for IP is valid or not
      * @param ipAddress ipAddress
      * @return boolean
-     * @throws  UnknownHostException UnknownHostException
+     * @throws  InvalidIPAddressException InvalidIPAddressException
      */
-    public static boolean isPublicIpAddress(final String ipAddress) throws UnknownHostException {
-        InetAddress address = InetAddress.getByName(ipAddress);
+    public static boolean isPublicIpAddress(final String ipAddress) {
+        InetAddress address;
+        try {
+            address = InetAddress.getByName(ipAddress);
+        } catch (final UnknownHostException e) {
+            throw new InvalidIPAddressException(e.getMessage());
+        }
         if (address instanceof Inet6Address || address instanceof Inet4Address) {
             return !address.isSiteLocalAddress() && !address.isLoopbackAddress();
         }
diff --git a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheck.java b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheck.java
index 1e75d4c4ca..907225a4d8 100644
--- a/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheck.java
+++ b/data-prepper-plugins/geoip-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheck.java
@@ -10,23 +10,19 @@
 import java.io.File;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.List;
 
 
-/**cls
+/**
  * 
  * Implementation of class logic to check maxmind database id free or enterprise version
  */
 public class LicenseTypeCheck {
-
-    protected static final String[] DEFAULT_DATABASE_FILENAMES = new String[] { "GeoLite2-ASN.mmdb", "GeoLite2-City.mmdb", "GeoLite2-Country.mmdb" };
-    protected static final List<String> geoLite2Database = Arrays.asList(DEFAULT_DATABASE_FILENAMES);
-    private static final String geoIP2EnterpriseDB = "GeoIP2-Enterprise.mmdb";
+    private static final String GEOIP2_DATABASE = "geoip2";
+    private static final String GEOLITE2_DATABASE = "geolite2";
+    private static final String MMDB = "mmdb";
 
 
     public LicenseTypeCheck() {
-
     }
 
     /**
@@ -36,31 +32,30 @@ public LicenseTypeCheck() {
      * @return license type options
      */
     public LicenseTypeOptions isGeoLite2OrEnterpriseLicense(final String databasePath) {
-        LicenseTypeOptions licenseTypeOptions = LicenseTypeOptions.ENTERPRISE;
-        File directory = new File(databasePath);
-        // list all files present in the directory
-        File[] files = directory.listFiles();
-
-        for(File file : files) {
-            // convert the file name into string
-            String fileName = file.toString();
-
-            int index = fileName.lastIndexOf('.');
-            if(index > 0) {
-                String extension = fileName.substring(index + 1);
-                Path onlyFileName = Paths.get(fileName).getFileName();
-
-                if((extension.equals("mmdb")) && (geoIP2EnterpriseDB.equals(onlyFileName.toString()))) {
-                    licenseTypeOptions = LicenseTypeOptions.ENTERPRISE;
-                    break;
-                }
-                else if((extension.equals("mmdb")) && (geoLite2Database.contains(onlyFileName.toString())))
-                {
-                    licenseTypeOptions = LicenseTypeOptions.FREE;
+        LicenseTypeOptions licenseTypeOptions = null;
+        final File directory = new File(databasePath);
+        if (directory.isDirectory()) {
+            // list all files present in the directory
+            final File[] files = directory.listFiles();
+
+            for (final File file : files) {
+                // convert the file name into string
+                final String fileName = file.toString();
+
+                int index = fileName.lastIndexOf('.');
+                if (index > 0) {
+                    String extension = fileName.substring(index + 1);
+                    Path onlyFileName = Paths.get(fileName).getFileName();
+
+                    if ((extension.equals(MMDB)) && (onlyFileName.toString().toLowerCase().contains(GEOIP2_DATABASE))) {
+                        licenseTypeOptions = LicenseTypeOptions.ENTERPRISE;
+                        break;
+                    } else if ((extension.equals(MMDB)) && (onlyFileName.toString().toLowerCase().contains(GEOLITE2_DATABASE))) {
+                        licenseTypeOptions = LicenseTypeOptions.FREE;
+                    }
                 }
             }
         }
         return licenseTypeOptions;
     }
 }
-
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPFieldTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPFieldTest.java
new file mode 100644
index 0000000000..4a4896304c
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPFieldTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+class GeoIPFieldTest {
+
+    @Test
+    void test_findByName_should_return_geoip_field_if_valid() {
+        final GeoIPField geoIPField = GeoIPField.findByName("city_name");
+        assertThat(geoIPField, equalTo(GeoIPField.CITY_NAME));
+    }
+
+    @Test
+    void test_findByName_should_return_null_if_invalid() {
+        final GeoIPField geoIPField = GeoIPField.findByName("coordinates");
+        assertThat(geoIPField, equalTo(null));
+    }
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorTest.java
index 55d28b566b..1ff06cfc99 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/GeoIPProcessorTest.java
@@ -10,7 +10,10 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.opensearch.dataprepper.expression.ExpressionEvaluator;
 import org.opensearch.dataprepper.metrics.PluginMetrics;
@@ -19,30 +22,59 @@
 import org.opensearch.dataprepper.model.log.JacksonLog;
 import org.opensearch.dataprepper.model.record.Record;
 import org.opensearch.dataprepper.plugins.processor.configuration.EntryConfig;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.EnrichFailedException;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.exception.EnrichFailedException;
 import org.opensearch.dataprepper.plugins.processor.extension.GeoIPProcessorService;
 import org.opensearch.dataprepper.plugins.processor.extension.GeoIpConfigSupplier;
-import org.opensearch.dataprepper.test.helper.ReflectivelySetField;
+import org.opensearch.dataprepper.plugins.processor.utils.IPValidationCheck;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CITY_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CONTINENT_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CONTINENT_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.COUNTRY_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.IS_COUNTRY_IN_EUROPEAN_UNION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LATITUDE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LOCATION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LOCATION_ACCURACY_RADIUS;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LONGITUDE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.METRO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.POSTAL_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.POSTAL_CODE_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REGISTERED_COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REGISTERED_COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_TYPE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.TIME_ZONE;
 import static org.opensearch.dataprepper.plugins.processor.GeoIPProcessor.GEO_IP_EVENTS_FAILED_LOOKUP;
 import static org.opensearch.dataprepper.plugins.processor.GeoIPProcessor.GEO_IP_EVENTS_PROCESSED;
 
@@ -66,10 +98,15 @@ class GeoIPProcessorTest {
     private Counter geoIpEventsProcessed;
     @Mock
     private Counter geoIpEventsFailedLookup;
+    @Mock
+    private GeoIPDatabaseReader geoIPDatabaseReader;
+    @Captor
+    private ArgumentCaptor<List<GeoIPField>> geoIPFieldCaptor;
 
     @BeforeEach
     void setUp() {
-        when(geoIpConfigSupplier.getGeoIPProcessorService()).thenReturn(geoIPProcessorService);
+        when(geoIpConfigSupplier.getGeoIPProcessorService()).thenReturn(Optional.of(geoIPProcessorService));
+        lenient().when(geoIPProcessorService.getGeoIPDatabaseReader()).thenReturn(geoIPDatabaseReader);
         lenient().when(pluginMetrics.counter(GEO_IP_EVENTS_PROCESSED)).thenReturn(geoIpEventsProcessed);
         lenient().when(pluginMetrics.counter(GEO_IP_EVENTS_FAILED_LOOKUP)).thenReturn(geoIpEventsFailedLookup);
     }
@@ -85,20 +122,18 @@ private GeoIPProcessor createObjectUnderTest() {
     }
 
     @Test
-    void doExecuteTest_with_when_condition_should_only_enrich_events_that_match_when_condition() throws NoSuchFieldException, IllegalAccessException {
+    void doExecuteTest_with_when_condition_should_only_enrich_events_that_match_when_condition() {
         final String whenCondition = "/peer/status == success";
 
         when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
         when(geoIPProcessorConfig.getWhenCondition()).thenReturn(whenCondition);
         when(entry.getSource()).thenReturn("/peer/ip");
         when(entry.getTarget()).thenReturn(TARGET);
-        when(entry.getFields()).thenReturn(setFields());
+        when(entry.getIncludeFields()).thenReturn(setFields());
 
         final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
 
-        when(geoIPProcessorService.getGeoData(any(), any())).thenReturn(prepareGeoData());
-
-        ReflectivelySetField.setField(GeoIPProcessor.class, geoIPProcessor, "geoIPProcessorService", geoIPProcessorService);
+        when(geoIPDatabaseReader.getGeoData(any(), any(), any())).thenReturn(prepareGeoData());
 
         final Record<Event> record1 = createCustomRecord("success");
         final Record<Event> record2 = createCustomRecord("failed");
@@ -126,38 +161,154 @@ void doExecuteTest_with_when_condition_should_only_enrich_events_that_match_when
     }
 
     @Test
-    void doExecuteTest_should_add_geo_data_to_event_if_source_is_non_null() throws NoSuchFieldException, IllegalAccessException {
+    void doExecuteTest_should_add_geo_data_to_event_if_source_is_non_null() {
+        when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
+        when(entry.getSource()).thenReturn(SOURCE);
+        when(entry.getTarget()).thenReturn(TARGET);
+        when(entry.getIncludeFields()).thenReturn(setFields());
+
+        final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
+
+        when(geoIPDatabaseReader.getGeoData(any(), any(), any())).thenReturn(prepareGeoData());
+        Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
+        for (final Record<Event> record : records) {
+            final Event event = record.getData();
+            assertThat(event.get("/peer/ip", String.class), equalTo("136.226.242.205"));
+            assertThat(event.containsKey(TARGET), equalTo(true));
+            verify(geoIpEventsProcessed).increment();
+        }
+    }
+
+    @Test
+    void doExecuteTest_should_add_geo_data_with_expected_fields_to_event_when_include_fields_is_configured() {
         when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
         when(entry.getSource()).thenReturn(SOURCE);
         when(entry.getTarget()).thenReturn(TARGET);
-        when(entry.getFields()).thenReturn(setFields());
+
+        final List<String> includeFields = List.of("city_name", "asn");
+        final List<GeoIPField> includeFieldsResult = List.of(GeoIPField.CITY_NAME, GeoIPField.ASN);
+        when(entry.getIncludeFields()).thenReturn(includeFields);
 
         final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
 
-        when(geoIPProcessorService.getGeoData(any(), any())).thenReturn(prepareGeoData());
-        ReflectivelySetField.setField(GeoIPProcessor.class, geoIPProcessor,
-                "geoIPProcessorService", geoIPProcessorService);
+        when(geoIPDatabaseReader.getGeoData(any(), any(), any())).thenReturn(prepareGeoData());
         Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
+        verify(geoIPDatabaseReader).getGeoData(any(), geoIPFieldCaptor.capture(), any());
+
         for (final Record<Event> record : records) {
             final Event event = record.getData();
             assertThat(event.get("/peer/ip", String.class), equalTo("136.226.242.205"));
-            assertThat(event.containsKey("geolocation"), equalTo(true));
+            assertThat(event.containsKey(TARGET), equalTo(true));
             verify(geoIpEventsProcessed).increment();
         }
+
+        final List<GeoIPField> value = geoIPFieldCaptor.getValue();
+        assertThat(value, containsInAnyOrder(includeFieldsResult.toArray()));
     }
 
     @Test
-    void doExecuteTest_should_not_add_geo_data_to_event_if_source_is_null() throws NoSuchFieldException, IllegalAccessException {
+    void doExecuteTest_should_add_geo_data_with_expected_fields_to_event_when_exclude_fields_is_configured() {
+        when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
+        when(entry.getSource()).thenReturn(SOURCE);
+        when(entry.getTarget()).thenReturn(TARGET);
+
+        final List<String> excludeFields = List.of("country_name", "country_iso_code", "city_name", "asn", "asn_organization", "network", "ip");
+        final List<GeoIPField> excludeFieldsResult = List.of(CONTINENT_NAME, CONTINENT_CODE, IS_COUNTRY_IN_EUROPEAN_UNION,
+                REPRESENTED_COUNTRY_NAME, REPRESENTED_COUNTRY_ISO_CODE, REPRESENTED_COUNTRY_TYPE, REGISTERED_COUNTRY_NAME,
+                REGISTERED_COUNTRY_ISO_CODE, LOCATION, LOCATION_ACCURACY_RADIUS, LATITUDE, LONGITUDE, METRO_CODE, TIME_ZONE, POSTAL_CODE,
+                MOST_SPECIFIED_SUBDIVISION_NAME, MOST_SPECIFIED_SUBDIVISION_ISO_CODE, LEAST_SPECIFIED_SUBDIVISION_NAME,
+                LEAST_SPECIFIED_SUBDIVISION_ISO_CODE, COUNTRY_CONFIDENCE, CITY_CONFIDENCE, MOST_SPECIFIED_SUBDIVISION_CONFIDENCE,
+                LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE, POSTAL_CODE_CONFIDENCE);
+        when(entry.getExcludeFields()).thenReturn(excludeFields);
+
+        final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
+
+        when(geoIPDatabaseReader.getGeoData(any(), any(), any())).thenReturn(prepareGeoData());
+        Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
+        verify(geoIPDatabaseReader).getGeoData(any(), geoIPFieldCaptor.capture(), any());
+
+        for (final Record<Event> record : records) {
+            final Event event = record.getData();
+            assertThat(event.get("/peer/ip", String.class), equalTo("136.226.242.205"));
+            assertThat(event.containsKey(TARGET), equalTo(true));
+            verify(geoIpEventsProcessed).increment();
+        }
+
+        final List<GeoIPField> value = geoIPFieldCaptor.getValue();
+        assertThat(value, containsInAnyOrder(excludeFieldsResult.toArray()));
+    }
+
+    @Test
+    void doExecuteTest_should_not_add_geo_data_to_event_if_source_is_null() {
         when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
         when(entry.getSource()).thenReturn("ip");
-        when(entry.getFields()).thenReturn(setFields());
+        when(entry.getIncludeFields()).thenReturn(setFields());
 
         final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
 
-        ReflectivelySetField.setField(GeoIPProcessor.class, geoIPProcessor,
-                "geoIPProcessorService", geoIPProcessorService);
         Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
 
+        for (final Record<Event> record : records) {
+            final Event event = record.getData();
+            assertThat(!event.containsKey("geo"), equalTo(true));
+        }
+
+        verify(geoIpEventsProcessed).increment();
+        verify(geoIpEventsFailedLookup).increment();
+    }
+
+    @Test
+    void doExecuteTest_should_not_add_geo_data_to_event_if_returned_data_is_empty() {
+        when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
+        when(entry.getSource()).thenReturn(SOURCE);
+        when(entry.getIncludeFields()).thenReturn(setFields());
+
+        final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
+
+        when(geoIPDatabaseReader.getGeoData(any(), any(), any())).thenReturn(Collections.EMPTY_MAP);
+        Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
+        for (final Record<Event> record : records) {
+            final Event event = record.getData();
+            assertThat(!event.containsKey("geo"), equalTo(true));
+        }
+
+        verify(geoIpEventsProcessed).increment();
+        verify(geoIpEventsFailedLookup).increment();
+    }
+
+    @Test
+    void doExecuteTest_should_not_add_geodata_if_database_is_expired() {
+        when(geoIPDatabaseReader.isExpired()).thenReturn(true);
+
+        final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
+
+        final Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
+        for (final Record<Event> record : records) {
+            final Event event = record.getData();
+            assertThat(!event.containsKey("geo"), equalTo(true));
+        }
+
+        verify(geoIpEventsProcessed).increment();
+    }
+
+    @Test
+    void doExecuteTest_should_not_add_geodata_if_ip_address_is_not_public() {
+        try (final MockedStatic<IPValidationCheck> ipValidationCheckMockedStatic = mockStatic(IPValidationCheck.class)) {
+            ipValidationCheckMockedStatic.when(() -> IPValidationCheck.isPublicIpAddress(any())).thenReturn(false);
+        }
+
+        when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
+        when(entry.getSource()).thenReturn(SOURCE);
+        when(entry.getIncludeFields()).thenReturn(setFields());
+
+        final GeoIPProcessor geoIPProcessor = createObjectUnderTest();
+
+        final Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
+        for (final Record<Event> record : records) {
+            final Event event = record.getData();
+            assertThat(!event.containsKey("geo"), equalTo(true));
+        }
+
         verify(geoIpEventsProcessed).increment();
         verify(geoIpEventsFailedLookup).increment();
     }
@@ -165,17 +316,15 @@ void doExecuteTest_should_not_add_geo_data_to_event_if_source_is_null() throws N
     @Test
     void test_tags_when_enrich_fails() {
         when(entry.getSource()).thenReturn(SOURCE);
-        when(entry.getFields()).thenReturn(setFields());
+        when(entry.getIncludeFields()).thenReturn(setFields());
 
         List<String> testTags = List.of("tag1", "tag2");
         when(geoIPProcessorConfig.getTagsOnFailure()).thenReturn(testTags);
         when(geoIPProcessorConfig.getEntries()).thenReturn(List.of(entry));
 
-        when(geoIpConfigSupplier.getGeoIPProcessorService()).thenReturn(geoIPProcessorService);
-
         GeoIPProcessor geoIPProcessor = createObjectUnderTest();
 
-        doThrow(EnrichFailedException.class).when(geoIPProcessorService).getGeoData(any(), any());
+        doThrow(EnrichFailedException.class).when(geoIPDatabaseReader).getGeoData(any(), any(), any());
 
         Collection<Record<Event>> records = geoIPProcessor.doExecute(setEventQueue());
 
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfigTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfigTest.java
index 129dcb7370..8b095ffb63 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfigTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/configuration/EntryConfigTest.java
@@ -5,7 +5,6 @@
 
 package org.opensearch.dataprepper.plugins.processor.configuration;
 
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import org.opensearch.dataprepper.test.helper.ReflectivelySetField;
@@ -19,32 +18,77 @@
 import static org.opensearch.dataprepper.plugins.processor.configuration.EntryConfig.DEFAULT_TARGET;
 
 class EntryConfigTest {
-    private EntryConfig entryConfig;
 
-    @BeforeEach
-    void setUp() {
-        entryConfig = new EntryConfig();
+    private EntryConfig createObjectUnderTest() {
+        return new EntryConfig();
     }
 
     @Test
     void testDefaultConfig() {
+        final EntryConfig entryConfig = createObjectUnderTest();
+
         assertThat(entryConfig.getSource(), is(nullValue()));
         assertThat(entryConfig.getTarget(), equalTo(DEFAULT_TARGET));
-        assertThat(entryConfig.getFields(), is(nullValue()));
+        assertThat(entryConfig.getIncludeFields(), equalTo(null));
+        assertThat(entryConfig.getExcludeFields(), equalTo(null));
     }
 
     @Test
     void testCustomConfig() throws NoSuchFieldException, IllegalAccessException {
+        final EntryConfig entryConfig = createObjectUnderTest();
+
         final String sourceValue = "source";
         final String targetValue = "target";
-        final List<String> fieldsValue = List.of("city", "country");
+        final List<String> includeFieldsValue = List.of("city_name");
+        final List<String> excludeFieldsValue = List.of("asn");
 
         ReflectivelySetField.setField(EntryConfig.class, entryConfig, "source", sourceValue);
         ReflectivelySetField.setField(EntryConfig.class, entryConfig, "target", targetValue);
-        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "fields", fieldsValue);
+        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "includeFields", includeFieldsValue);
+        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "excludeFields", excludeFieldsValue);
 
         assertThat(entryConfig.getSource(), equalTo(sourceValue));
         assertThat(entryConfig.getTarget(), equalTo(targetValue));
-        assertThat(entryConfig.getFields(), equalTo(fieldsValue));
+        assertThat(entryConfig.getIncludeFields(), equalTo(includeFieldsValue));
+        assertThat(entryConfig.getExcludeFields(), equalTo(excludeFieldsValue));
+    }
+
+    @Test
+    void test_areFieldsValid_should_return_true_if_only_include_fields_is_configured() throws NoSuchFieldException, IllegalAccessException {
+        final EntryConfig entryConfig = createObjectUnderTest();
+        final List<String> includeFields = List.of("city_name", "continent_code");
+
+        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "includeFields", includeFields);
+
+        assertThat(entryConfig.areFieldsValid(), equalTo(true));
+    }
+
+    @Test
+    void test_areFieldsValid_should_return_true_if_only_exclude_fields_is_configured() throws NoSuchFieldException, IllegalAccessException {
+        final EntryConfig entryConfig = createObjectUnderTest();
+        final List<String> excludeFields = List.of("city_name", "continent_code");
+
+        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "excludeFields", excludeFields);
+
+        assertThat(entryConfig.areFieldsValid(), equalTo(true));
+    }
+
+    @Test
+    void test_areFieldsValid_should_return_false_if_both_include_and_exclude_fields_are_configured() throws NoSuchFieldException, IllegalAccessException {
+        final EntryConfig entryConfig = createObjectUnderTest();
+        final List<String> includeFields = List.of("city_name", "continent_code");
+        final List<String> excludeFields = List.of("asn");
+
+        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "includeFields", includeFields);
+        ReflectivelySetField.setField(EntryConfig.class, entryConfig, "excludeFields", excludeFields);
+
+        assertThat(entryConfig.areFieldsValid(), equalTo(false));
+    }
+
+    @Test
+    void test_areFieldsValid_should_return_false_if_both_include_and_exclude_fields_are_not_configured() {
+        final EntryConfig entryConfig = createObjectUnderTest();
+
+        assertThat(entryConfig.areFieldsValid(), equalTo(false));
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIP2DatabaseReaderTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIP2DatabaseReaderTest.java
new file mode 100644
index 0000000000..c438f3c0b2
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoIP2DatabaseReaderTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+
+import com.maxmind.db.Metadata;
+import com.maxmind.db.Network;
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.AsnResponse;
+import com.maxmind.geoip2.model.EnterpriseResponse;
+import com.maxmind.geoip2.record.City;
+import com.maxmind.geoip2.record.Continent;
+import com.maxmind.geoip2.record.Country;
+import com.maxmind.geoip2.record.Location;
+import com.maxmind.geoip2.record.Postal;
+import com.maxmind.geoip2.record.RepresentedCountry;
+import com.maxmind.geoip2.record.Subdivision;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.opensearch.dataprepper.plugins.processor.GeoIPDatabase;
+import org.opensearch.dataprepper.plugins.processor.GeoIPField;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderBuilder;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPFileManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.ASN;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CITY_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CITY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CONTINENT_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CONTINENT_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.COUNTRY_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.IP;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.IS_COUNTRY_IN_EUROPEAN_UNION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LOCATION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LOCATION_ACCURACY_RADIUS;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.METRO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.NETWORK;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.ASN_ORGANIZATION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.POSTAL_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.POSTAL_CODE_CONFIDENCE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REGISTERED_COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REGISTERED_COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_TYPE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.TIME_ZONE;
+import static org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader.LAT;
+import static org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader.LON;
+
+@ExtendWith(MockitoExtension.class)
+class GeoIP2DatabaseReaderTest {
+    public static final String GEOIP2_TEST_MMDB_FILES = "./build/resources/test/mmdb-files/geo-ip2";
+    public static final long ASN_RESULT = 12345L;
+    public static final String ASN_ORG_RESULT = "Example Org";
+    private static final String NETWORK_RESULT = "1.2.3.0/24";
+    private static final String COUNTRY_NAME_RESULT = "United States";
+    private static final String COUNTRY_ISO_CODE_RESULT = "US";
+    private static final Boolean COUNTRY_IS_IN_EUROPEAN_UNION_RESULT = false;
+    private static final String CITY_NAME_RESULT = "Los Angeles";
+    private static final Double LATITUDE_RESULT = 12.34;
+    private static final Double LONGITUDE_RESULT = 56.78;
+    private static final String TIME_ZONE_RESULT = "America/Los_Angeles";
+    private static final Integer METRO_CODE_RESULT = 807;
+    private static final String POSTAL_CODE_RESULT = "90001";
+    private static final String LEAST_SPECIFIED_SUBDIVISION_NAME_RESULT = "California";
+    private static final String MOST_SPECIFIED_SUBDIVISION_NAME_RESULT = "California";
+    private static final String LEAST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT = "CA";
+    private static final String MOST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT = "CA";
+    private static final String REGISTERED_COUNTRY_NAME_RESULT = "Argentina";
+    private static final String REGISTERED_COUNTRY_ISO_CODE_RESULT = "AR";
+    private static final String REPRESENTED_COUNTRY_NAME_RESULT = "Belgium";
+    private static final String REPRESENTED_COUNTRY_ISO_CODE_RESULT = "BE";
+    private static final String REPRESENTED_COUNTRY_TYPE_RESULT = "military";
+    private static final String CONTINENT_NAME_RESULT = "North America";
+    private static final String CONTINENT_CODE_RESULT = "123456";
+    private static final Map<String, Object> LOCATION_RESULT = Map.of(LAT, LATITUDE_RESULT, LON, LONGITUDE_RESULT);
+    private static final Integer COUNTRY_CONFIDENCE_RESULT = 100;
+    private static final Integer CITY_CONFIDENCE_RESULT = 90;
+    private static final Integer LOCATION_ACCURACY_RADIUS_RESULT = 10;
+    private static final Integer POSTAL_CODE_CONFIDENCE_RESULT = 85;
+    private static final Integer MOST_SPECIFIED_SUBDIVISION_CONFIDENCE_RESULT = 75;
+    private static final Integer LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE_RESULT = 60;
+    private static final String IP_RESULT = "1.2.3.4";
+
+    @Mock
+    private DatabaseReaderBuilder databaseReaderBuilder;
+    @Mock
+    private DatabaseReader enterpriseDatabaseReader;
+    @Mock
+    private Metadata metadata;
+    @Mock
+    private EnterpriseResponse enterpriseResponse;
+    @Mock
+    private AsnResponse asnResponse;
+    @Mock
+    private Continent continent;
+    @Mock
+    private Country country;
+    @Mock
+    private RepresentedCountry representedCountry;
+    @Mock
+    private Country registeredCountry;
+    @Mock
+    private City city;
+    @Mock
+    private Location location;
+    @Mock
+    private Network network;
+    @Mock
+    private Postal postal;
+    @Mock
+    private Subdivision leastSpecificSubdivision;
+    @Mock
+    private Subdivision mostSpecificSubdivision;
+    @Mock
+    private InetAddress inetAddress;
+    @Mock
+    private GeoIPFileManager geoIPFileManager;
+
+    @BeforeEach
+    void setUp() throws IOException {
+        when(databaseReaderBuilder.buildReader(Path.of(GEOIP2_TEST_MMDB_FILES + File.separator + "GeoIP2-Enterprise-Test.mmdb"), 0))
+                .thenReturn(enterpriseDatabaseReader);
+
+        when(enterpriseDatabaseReader.getMetadata()).thenReturn(metadata);
+        final Date date = new Date(9949107436565L);
+        when(metadata.getBuildDate()).thenReturn(date);
+
+        lenient().when(enterpriseResponse.getContinent()).thenReturn(continent);
+        lenient().when(continent.getName()).thenReturn(CONTINENT_NAME_RESULT);
+        lenient().when(continent.getCode()).thenReturn(CONTINENT_CODE_RESULT);
+
+        lenient().when(enterpriseResponse.getCountry()).thenReturn(country);
+        lenient().when(country.getName()).thenReturn(COUNTRY_NAME_RESULT);
+        lenient().when(country.getIsoCode()).thenReturn(COUNTRY_ISO_CODE_RESULT);
+        lenient().when(country.isInEuropeanUnion()).thenReturn(COUNTRY_IS_IN_EUROPEAN_UNION_RESULT);
+        lenient().when(country.getConfidence()).thenReturn(COUNTRY_CONFIDENCE_RESULT);
+
+        lenient().when(enterpriseResponse.getRegisteredCountry()).thenReturn(registeredCountry);
+        lenient().when(registeredCountry.getName()).thenReturn(REGISTERED_COUNTRY_NAME_RESULT);
+        lenient().when(registeredCountry.getIsoCode()).thenReturn(REGISTERED_COUNTRY_ISO_CODE_RESULT);
+
+        lenient().when(enterpriseResponse.getRepresentedCountry()).thenReturn(representedCountry);
+        lenient().when(representedCountry.getName()).thenReturn(REPRESENTED_COUNTRY_NAME_RESULT);
+        lenient().when(representedCountry.getIsoCode()).thenReturn(REPRESENTED_COUNTRY_ISO_CODE_RESULT);
+        lenient().when(representedCountry.getType()).thenReturn(REPRESENTED_COUNTRY_TYPE_RESULT);
+
+        lenient().when(enterpriseResponse.getCity()).thenReturn(city);
+        lenient().when(city.getName()).thenReturn(CITY_NAME_RESULT);
+        lenient().when(city.getConfidence()).thenReturn(CITY_CONFIDENCE_RESULT);
+
+        lenient().when(enterpriseResponse.getLocation()).thenReturn(location);
+        lenient().when(location.getLatitude()).thenReturn(LATITUDE_RESULT);
+        lenient().when(location.getLongitude()).thenReturn(LONGITUDE_RESULT);
+        lenient().when(location.getMetroCode()).thenReturn(METRO_CODE_RESULT);
+        lenient().when(location.getTimeZone()).thenReturn(TIME_ZONE_RESULT);
+        lenient().when(location.getAccuracyRadius()).thenReturn(LOCATION_ACCURACY_RADIUS_RESULT);
+
+        lenient().when(enterpriseResponse.getPostal()).thenReturn(postal);
+        lenient().when(postal.getCode()).thenReturn(POSTAL_CODE_RESULT);
+        lenient().when(postal.getConfidence()).thenReturn(POSTAL_CODE_CONFIDENCE_RESULT);
+
+        lenient().when(enterpriseResponse.getLeastSpecificSubdivision()).thenReturn(leastSpecificSubdivision);
+        lenient().when(leastSpecificSubdivision.getName()).thenReturn(LEAST_SPECIFIED_SUBDIVISION_NAME_RESULT);
+        lenient().when(leastSpecificSubdivision.getIsoCode()).thenReturn(LEAST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT);
+        lenient().when(leastSpecificSubdivision.getConfidence()).thenReturn(LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE_RESULT);
+
+        lenient().when(enterpriseResponse.getMostSpecificSubdivision()).thenReturn(mostSpecificSubdivision);
+        lenient().when(mostSpecificSubdivision.getName()).thenReturn(MOST_SPECIFIED_SUBDIVISION_NAME_RESULT);
+        lenient().when(mostSpecificSubdivision.getIsoCode()).thenReturn(MOST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT);
+        lenient().when(mostSpecificSubdivision.getConfidence()).thenReturn(MOST_SPECIFIED_SUBDIVISION_CONFIDENCE_RESULT);
+
+        lenient().when(asnResponse.getAutonomousSystemNumber()).thenReturn(ASN_RESULT);
+        lenient().when(asnResponse.getAutonomousSystemOrganization()).thenReturn(ASN_ORG_RESULT);
+        lenient().when(asnResponse.getNetwork()).thenReturn(network);
+        lenient().when(asnResponse.getIpAddress()).thenReturn(IP_RESULT);
+        lenient().when(network.toString()).thenReturn(NETWORK_RESULT);
+    }
+
+    @AfterEach
+    void tearDown() {
+        verifyNoMoreInteractions(enterpriseDatabaseReader);
+    }
+
+    GeoIP2DatabaseReader createObjectUnderTest() {
+        return new GeoIP2DatabaseReader(databaseReaderBuilder, geoIPFileManager, GEOIP2_TEST_MMDB_FILES, 0);
+    }
+
+    @Test
+    void test_getGeoData_for_all_fields_in_enterprise_database_add_only_fields_configured() throws IOException, GeoIp2Exception {
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(CONTINENT_NAME, CONTINENT_CODE, COUNTRY_NAME, COUNTRY_ISO_CODE, IS_COUNTRY_IN_EUROPEAN_UNION,
+                COUNTRY_CONFIDENCE, REPRESENTED_COUNTRY_ISO_CODE, REGISTERED_COUNTRY_ISO_CODE, CITY_NAME, CITY_CONFIDENCE, LOCATION,
+                LOCATION_ACCURACY_RADIUS, METRO_CODE, TIME_ZONE, POSTAL_CODE, POSTAL_CODE_CONFIDENCE);
+        final Set<GeoIPDatabase> databases = Set.of(GeoIPDatabase.ENTERPRISE);
+
+        when(enterpriseDatabaseReader.tryEnterprise(inetAddress)).thenReturn(Optional.of(enterpriseResponse));
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.size(), equalTo(fields.size()));
+        assertThat(geoData.get(CONTINENT_NAME.getFieldName()), equalTo(CONTINENT_NAME_RESULT));
+        assertThat(geoData.get(CONTINENT_CODE.getFieldName()), equalTo(CONTINENT_CODE_RESULT));
+        assertThat(geoData.get(COUNTRY_NAME.getFieldName()), equalTo(COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(COUNTRY_ISO_CODE.getFieldName()), equalTo(COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(IS_COUNTRY_IN_EUROPEAN_UNION.getFieldName()), equalTo(COUNTRY_IS_IN_EUROPEAN_UNION_RESULT));
+        assertThat(geoData.get(COUNTRY_CONFIDENCE.getFieldName()), equalTo(COUNTRY_CONFIDENCE_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REGISTERED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REPRESENTED_COUNTRY_ISO_CODE_RESULT));
+
+        assertThat(geoData.get(CITY_NAME.getFieldName()), equalTo(CITY_NAME_RESULT));
+        assertThat(geoData.get(CITY_CONFIDENCE.getFieldName()), equalTo(CITY_CONFIDENCE_RESULT));
+        assertThat(geoData.get(LOCATION.getFieldName()), equalTo(LOCATION_RESULT));
+        assertThat(geoData.get(LOCATION_ACCURACY_RADIUS.getFieldName()), equalTo(LOCATION_ACCURACY_RADIUS_RESULT));
+        assertThat(geoData.get(METRO_CODE.getFieldName()), equalTo(METRO_CODE_RESULT));
+        assertThat(geoData.get(TIME_ZONE.getFieldName()), equalTo(TIME_ZONE_RESULT));
+        assertThat(geoData.get(POSTAL_CODE.getFieldName()), equalTo(POSTAL_CODE_RESULT));
+        assertThat(geoData.get(POSTAL_CODE_CONFIDENCE.getFieldName()), equalTo(POSTAL_CODE_CONFIDENCE_RESULT));
+    }
+
+    @Test
+    void test_getGeoData_for_all_fields_in_enterprise_database_when_no_fields_are_configured() throws IOException, GeoIp2Exception {
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+        final List<GeoIPField> fields = Collections.emptyList();
+        final Set<GeoIPDatabase> databases = Set.of(GeoIPDatabase.ENTERPRISE);
+
+        when(enterpriseDatabaseReader.tryEnterprise(inetAddress)).thenReturn(Optional.of(enterpriseResponse));
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.get(CONTINENT_NAME.getFieldName()), equalTo(CONTINENT_NAME_RESULT));
+        assertThat(geoData.get(CONTINENT_CODE.getFieldName()), equalTo(CONTINENT_CODE_RESULT));
+        assertThat(geoData.get(COUNTRY_NAME.getFieldName()), equalTo(COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(COUNTRY_CONFIDENCE.getFieldName()), equalTo(COUNTRY_CONFIDENCE_RESULT));
+        assertThat(geoData.get(COUNTRY_ISO_CODE.getFieldName()), equalTo(COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(IS_COUNTRY_IN_EUROPEAN_UNION.getFieldName()), equalTo(COUNTRY_IS_IN_EUROPEAN_UNION_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_NAME.getFieldName()), equalTo(REGISTERED_COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REGISTERED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_NAME.getFieldName()), equalTo(REPRESENTED_COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REPRESENTED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_TYPE.getFieldName()), equalTo(REPRESENTED_COUNTRY_TYPE_RESULT));
+
+        assertThat(geoData.get(CITY_NAME.getFieldName()), equalTo(CITY_NAME_RESULT));
+        assertThat(geoData.get(CITY_CONFIDENCE.getFieldName()), equalTo(CITY_CONFIDENCE_RESULT));
+        assertThat(geoData.get(LOCATION.getFieldName()), equalTo(LOCATION_RESULT));
+        assertThat(geoData.get(LOCATION_ACCURACY_RADIUS.getFieldName()), equalTo(LOCATION_ACCURACY_RADIUS_RESULT));
+        assertThat(geoData.get(METRO_CODE.getFieldName()), equalTo(METRO_CODE_RESULT));
+        assertThat(geoData.get(TIME_ZONE.getFieldName()), equalTo(TIME_ZONE_RESULT));
+        assertThat(geoData.get(POSTAL_CODE.getFieldName()), equalTo(POSTAL_CODE_RESULT));
+        assertThat(geoData.get(POSTAL_CODE_CONFIDENCE.getFieldName()), equalTo(POSTAL_CODE_CONFIDENCE_RESULT));
+        assertThat(geoData.get(MOST_SPECIFIED_SUBDIVISION_NAME.getFieldName()), equalTo(MOST_SPECIFIED_SUBDIVISION_NAME_RESULT));
+        assertThat(geoData.get(MOST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName()), equalTo(MOST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT));
+        assertThat(geoData.get(MOST_SPECIFIED_SUBDIVISION_CONFIDENCE.getFieldName()), equalTo(MOST_SPECIFIED_SUBDIVISION_CONFIDENCE_RESULT));
+        assertThat(geoData.get(LEAST_SPECIFIED_SUBDIVISION_NAME.getFieldName()), equalTo(LEAST_SPECIFIED_SUBDIVISION_NAME_RESULT));
+        assertThat(geoData.get(LEAST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName()), equalTo(LEAST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT));
+        assertThat(geoData.get(LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE.getFieldName()), equalTo(LEAST_SPECIFIED_SUBDIVISION_CONFIDENCE_RESULT));
+    }
+
+    @Test
+    void test_getGeoData_for_asn_fields_in_enterprise_database_when_no_fields_are_configured() throws IOException, GeoIp2Exception {
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+        final Set<GeoIPDatabase> databases = Set.of(GeoIPDatabase.ASN);
+        final List<GeoIPField> fields = List.of(ASN, ASN_ORGANIZATION, NETWORK, IP);
+
+        when(enterpriseDatabaseReader.tryAsn(inetAddress)).thenReturn(Optional.of(asnResponse));
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.size(), equalTo(fields.size()));
+        assertThat(geoData.get(ASN.getFieldName()), equalTo(ASN_RESULT));
+        assertThat(geoData.get(ASN_ORGANIZATION.getFieldName()), equalTo(ASN_ORG_RESULT));
+        assertThat(geoData.get(NETWORK.getFieldName()), equalTo(NETWORK_RESULT));
+        assertThat(geoData.get(IP.getFieldName()), equalTo(IP_RESULT));
+    }
+
+    @Test
+    void test_database_expired_should_return_false_when_expiry_date_is_in_future() {
+        final Date date = new Date(9949107436565L);
+        when(metadata.getBuildDate()).thenReturn(date);
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(false));
+    }
+
+    @Test
+    void test_database_expired_should_return_true_when_expiry_date_is_in_past() throws IOException {
+        final Date date = new Date(91911199999L);
+        when(metadata.getBuildDate()).thenReturn(date);
+        doNothing().when(geoIPFileManager).deleteDirectory(any());
+
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(true));
+        verify(enterpriseDatabaseReader).close();
+    }
+
+    @Test
+    void test_database_close_should_not_close_the_reader_unless_close_count_is_zero() {
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        objectUnderTest.retain();
+        objectUnderTest.close();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(false));
+    }
+
+    @Test
+    void test_database_close_should_close_the_reader_when_close_count_is_zero() throws IOException {
+        doNothing().when(geoIPFileManager).deleteDirectory(any());
+        final GeoIP2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        objectUnderTest.retain();
+        objectUnderTest.close();
+        objectUnderTest.close();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(false));
+        verify(enterpriseDatabaseReader).close();
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoLite2DatabaseReaderTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoLite2DatabaseReaderTest.java
new file mode 100644
index 0000000000..912a6eed6f
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GeoLite2DatabaseReaderTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.databaseenrich;
+
+import com.maxmind.db.Metadata;
+import com.maxmind.db.Network;
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.AsnResponse;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.model.CountryResponse;
+import com.maxmind.geoip2.record.City;
+import com.maxmind.geoip2.record.Continent;
+import com.maxmind.geoip2.record.Country;
+import com.maxmind.geoip2.record.Location;
+import com.maxmind.geoip2.record.Postal;
+import com.maxmind.geoip2.record.RepresentedCountry;
+import com.maxmind.geoip2.record.Subdivision;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.opensearch.dataprepper.plugins.processor.GeoIPDatabase;
+import org.opensearch.dataprepper.plugins.processor.GeoIPField;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPFileManager;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DatabaseReaderBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.file.Path;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPDatabase.CITY;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.ASN;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CITY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CONTINENT_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.CONTINENT_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.IP;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.IS_COUNTRY_IN_EUROPEAN_UNION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LATITUDE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LEAST_SPECIFIED_SUBDIVISION_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LOCATION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LOCATION_ACCURACY_RADIUS;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.LONGITUDE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.METRO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.MOST_SPECIFIED_SUBDIVISION_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.NETWORK;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.ASN_ORGANIZATION;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.POSTAL_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REGISTERED_COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REGISTERED_COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_ISO_CODE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_NAME;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.REPRESENTED_COUNTRY_TYPE;
+import static org.opensearch.dataprepper.plugins.processor.GeoIPField.TIME_ZONE;
+import static org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader.LAT;
+import static org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader.LON;
+
+
+@ExtendWith(MockitoExtension.class)
+class GeoLite2DatabaseReaderTest {
+    public static final String GEOLITE2_TEST_MMDB_FILES = "./build/resources/test/mmdb-files/geo-lite2";
+    public static final long ASN_RESULT = 12345L;
+    public static final String ASN_ORG_RESULT = "Example Org";
+    private static final String NETWORK_RESULT = "1.2.3.0/24";
+    private static final String COUNTRY_NAME_RESULT = "United States";
+    private static final String COUNTRY_ISO_CODE_RESULT = "US";
+    private static final Boolean COUNTRY_IS_IN_EUROPEAN_UNION_RESULT = false;
+    private static final String CITY_NAME_RESULT = "Los Angeles";
+    private static final Double LATITUDE_RESULT = 12.34;
+    private static final Double LONGITUDE_RESULT = 56.78;
+    private static final Integer LOCATION_ACCURACY_RADIUS_RESULT = 10;
+    private static final String TIME_ZONE_RESULT = "America/Los_Angeles";
+    private static final Integer METRO_CODE_RESULT = 807;
+    private static final String POSTAL_CODE_RESULT = "90001";
+    private static final String LEAST_SPECIFIED_SUBDIVISION_NAME_RESULT = "California";
+    private static final String MOST_SPECIFIED_SUBDIVISION_NAME_RESULT = "California";
+    private static final String LEAST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT = "CA";
+    private static final String MOST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT = "CA";
+    private static final String REGISTERED_COUNTRY_NAME_RESULT = "Argentina";
+    private static final String REGISTERED_COUNTRY_ISO_CODE_RESULT = "AR";
+    private static final String REPRESENTED_COUNTRY_NAME_RESULT = "Belgium";
+    private static final String REPRESENTED_COUNTRY_ISO_CODE_RESULT = "BE";
+    private static final String REPRESENTED_COUNTRY_TYPE_RESULT = "military";
+    private static final String CONTINENT_NAME_RESULT = "North America";
+    private static final String CONTINENT_CODE_RESULT = "123456";
+    private static final Map<String, Object> LOCATION_RESULT = Map.of(LAT, LATITUDE_RESULT, LON, LONGITUDE_RESULT);
+    private static final String IP_RESULT = "1.2.3.4";
+
+    @Mock
+    private DatabaseReaderBuilder databaseReaderBuilder;
+    @Mock
+    private DatabaseReader countryDatabaseReader;
+    @Mock
+    private DatabaseReader cityDatabaseReader;
+    @Mock
+    private DatabaseReader asnDatabaseReader;
+    @Mock
+    private Metadata metadata;
+    @Mock
+    private CountryResponse countryResponse;
+    @Mock
+    private CityResponse cityResponse;
+    @Mock
+    private AsnResponse asnResponse;
+    @Mock
+    private Continent continent;
+    @Mock
+    private Country country;
+    @Mock
+    private RepresentedCountry representedCountry;
+    @Mock
+    private Country registeredCountry;
+    @Mock
+    private City city;
+    @Mock
+    private Location location;
+    @Mock
+    private Network network;
+    @Mock
+    private Postal postal;
+    @Mock
+    private Subdivision leastSpecificSubdivision;
+    @Mock
+    private Subdivision mostSpecificSubdivision;
+    @Mock
+    private InetAddress inetAddress;
+    @Mock
+    private GeoIPFileManager geoIPFileManager;
+
+    @BeforeEach
+    void setUp() throws IOException {
+        when(databaseReaderBuilder.buildReader(Path.of(GEOLITE2_TEST_MMDB_FILES + File.separator + "GeoLite2-Country-Test.mmdb"), 0))
+                .thenReturn(countryDatabaseReader);
+        when(databaseReaderBuilder.buildReader(Path.of(GEOLITE2_TEST_MMDB_FILES + File.separator + "GeoLite2-City-Test.mmdb"), 0))
+                .thenReturn(cityDatabaseReader);
+        when(databaseReaderBuilder.buildReader(Path.of(GEOLITE2_TEST_MMDB_FILES + File.separator + "GeoLite2-ASN-Test.mmdb"), 0))
+                .thenReturn(asnDatabaseReader);
+
+        when(countryDatabaseReader.getMetadata()).thenReturn(metadata);
+        when(cityDatabaseReader.getMetadata()).thenReturn(metadata);
+        when(asnDatabaseReader.getMetadata()).thenReturn(metadata);
+        final Date date = new Date(9949107436565L);
+        when(metadata.getBuildDate()).thenReturn(date);
+
+        lenient().when(countryResponse.getContinent()).thenReturn(continent);
+        lenient().when(continent.getName()).thenReturn(CONTINENT_NAME_RESULT);
+        lenient().when(continent.getCode()).thenReturn(CONTINENT_CODE_RESULT);
+
+        lenient().when(countryResponse.getCountry()).thenReturn(country);
+        lenient().when(country.getName()).thenReturn(COUNTRY_NAME_RESULT);
+        lenient().when(country.getIsoCode()).thenReturn(COUNTRY_ISO_CODE_RESULT);
+        lenient().when(country.isInEuropeanUnion()).thenReturn(COUNTRY_IS_IN_EUROPEAN_UNION_RESULT);
+
+        lenient().when(countryResponse.getRegisteredCountry()).thenReturn(registeredCountry);
+        lenient().when(registeredCountry.getName()).thenReturn(REGISTERED_COUNTRY_NAME_RESULT);
+        lenient().when(registeredCountry.getIsoCode()).thenReturn(REGISTERED_COUNTRY_ISO_CODE_RESULT);
+
+        lenient().when(countryResponse.getRepresentedCountry()).thenReturn(representedCountry);
+        lenient().when(representedCountry.getName()).thenReturn(REPRESENTED_COUNTRY_NAME_RESULT);
+        lenient().when(representedCountry.getIsoCode()).thenReturn(REPRESENTED_COUNTRY_ISO_CODE_RESULT);
+        lenient().when(representedCountry.getType()).thenReturn(REPRESENTED_COUNTRY_TYPE_RESULT);
+
+        lenient().when(cityResponse.getCity()).thenReturn(city);
+        lenient().when(city.getName()).thenReturn(CITY_NAME_RESULT);
+
+        lenient().when(cityResponse.getContinent()).thenReturn(continent);
+        lenient().when(cityResponse.getCountry()).thenReturn(country);
+        lenient().when(cityResponse.getRegisteredCountry()).thenReturn(registeredCountry);
+        lenient().when(cityResponse.getRepresentedCountry()).thenReturn(representedCountry);
+
+        lenient().when(cityResponse.getLocation()).thenReturn(location);
+        lenient().when(location.getLatitude()).thenReturn(LATITUDE_RESULT);
+        lenient().when(location.getLongitude()).thenReturn(LONGITUDE_RESULT);
+        lenient().when(location.getMetroCode()).thenReturn(METRO_CODE_RESULT);
+        lenient().when(location.getTimeZone()).thenReturn(TIME_ZONE_RESULT);
+        lenient().when(location.getAccuracyRadius()).thenReturn(LOCATION_ACCURACY_RADIUS_RESULT);
+
+        lenient().when(cityResponse.getPostal()).thenReturn(postal);
+        lenient().when(postal.getCode()).thenReturn(POSTAL_CODE_RESULT);
+
+        lenient().when(cityResponse.getLeastSpecificSubdivision()).thenReturn(leastSpecificSubdivision);
+        lenient().when(leastSpecificSubdivision.getName()).thenReturn(LEAST_SPECIFIED_SUBDIVISION_NAME_RESULT);
+        lenient().when(leastSpecificSubdivision.getIsoCode()).thenReturn(LEAST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT);
+
+        lenient().when(cityResponse.getMostSpecificSubdivision()).thenReturn(mostSpecificSubdivision);
+        lenient().when(mostSpecificSubdivision.getName()).thenReturn(MOST_SPECIFIED_SUBDIVISION_NAME_RESULT);
+        lenient().when(mostSpecificSubdivision.getIsoCode()).thenReturn(MOST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT);
+
+        lenient().when(asnResponse.getAutonomousSystemNumber()).thenReturn(ASN_RESULT);
+        lenient().when(asnResponse.getAutonomousSystemOrganization()).thenReturn(ASN_ORG_RESULT);
+        lenient().when(asnResponse.getNetwork()).thenReturn(network);
+        lenient().when(asnResponse.getIpAddress()).thenReturn(IP_RESULT);
+        lenient().when(network.toString()).thenReturn(NETWORK_RESULT);
+    }
+
+    @AfterEach
+    void tearDown() {
+        verifyNoMoreInteractions(countryDatabaseReader);
+        verifyNoMoreInteractions(cityDatabaseReader);
+        verifyNoMoreInteractions(asnDatabaseReader);
+    }
+
+    GeoLite2DatabaseReader createObjectUnderTest() {
+        return new GeoLite2DatabaseReader(databaseReaderBuilder, geoIPFileManager, GEOLITE2_TEST_MMDB_FILES, 0);
+    }
+
+    @Test
+    void test_getGeoData_for_all_fields_in_country_database() throws IOException, GeoIp2Exception {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(CONTINENT_NAME, CONTINENT_CODE, COUNTRY_NAME, COUNTRY_ISO_CODE, IS_COUNTRY_IN_EUROPEAN_UNION,
+                REPRESENTED_COUNTRY_NAME, REPRESENTED_COUNTRY_ISO_CODE, REPRESENTED_COUNTRY_TYPE, REGISTERED_COUNTRY_NAME,
+                REGISTERED_COUNTRY_ISO_CODE);
+        final Set<GeoIPDatabase> databases = Set.of(GeoIPDatabase.COUNTRY);
+
+        when(countryDatabaseReader.tryCountry(inetAddress)).thenReturn(Optional.of(countryResponse));
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+        assertThat(geoData.size(), equalTo(fields.size()));
+        assertThat(geoData.get(CONTINENT_NAME.getFieldName()), equalTo(CONTINENT_NAME_RESULT));
+        assertThat(geoData.get(CONTINENT_CODE.getFieldName()), equalTo(CONTINENT_CODE_RESULT));
+        assertThat(geoData.get(COUNTRY_NAME.getFieldName()), equalTo(COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(COUNTRY_ISO_CODE.getFieldName()), equalTo(COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(IS_COUNTRY_IN_EUROPEAN_UNION.getFieldName()), equalTo(COUNTRY_IS_IN_EUROPEAN_UNION_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_NAME.getFieldName()), equalTo(REGISTERED_COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REGISTERED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_NAME.getFieldName()), equalTo(REPRESENTED_COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REPRESENTED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_TYPE.getFieldName()), equalTo(REPRESENTED_COUNTRY_TYPE_RESULT));
+    }
+
+    @Test
+    void test_getGeoData_for_all_fields_in_city_database() throws IOException, GeoIp2Exception {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(CONTINENT_NAME, CONTINENT_CODE, COUNTRY_NAME, COUNTRY_ISO_CODE, IS_COUNTRY_IN_EUROPEAN_UNION,
+                REPRESENTED_COUNTRY_NAME, REPRESENTED_COUNTRY_ISO_CODE, REPRESENTED_COUNTRY_TYPE, REGISTERED_COUNTRY_NAME,
+                REGISTERED_COUNTRY_ISO_CODE, CITY_NAME, LOCATION, LOCATION_ACCURACY_RADIUS, LATITUDE, LONGITUDE, METRO_CODE, TIME_ZONE, POSTAL_CODE,
+                MOST_SPECIFIED_SUBDIVISION_NAME, MOST_SPECIFIED_SUBDIVISION_ISO_CODE, LEAST_SPECIFIED_SUBDIVISION_NAME,
+                LEAST_SPECIFIED_SUBDIVISION_ISO_CODE);
+        final Set<GeoIPDatabase> databases = Set.of(CITY);
+
+        when(cityDatabaseReader.tryCity(inetAddress)).thenReturn(Optional.of(cityResponse));
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.size(), equalTo(fields.size()));
+        assertThat(geoData.get(CONTINENT_NAME.getFieldName()), equalTo(CONTINENT_NAME_RESULT));
+        assertThat(geoData.get(CONTINENT_CODE.getFieldName()), equalTo(CONTINENT_CODE_RESULT));
+        assertThat(geoData.get(COUNTRY_NAME.getFieldName()), equalTo(COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(COUNTRY_ISO_CODE.getFieldName()), equalTo(COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(IS_COUNTRY_IN_EUROPEAN_UNION.getFieldName()), equalTo(COUNTRY_IS_IN_EUROPEAN_UNION_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_NAME.getFieldName()), equalTo(REGISTERED_COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(REGISTERED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REGISTERED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_NAME.getFieldName()), equalTo(REPRESENTED_COUNTRY_NAME_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_ISO_CODE.getFieldName()), equalTo(REPRESENTED_COUNTRY_ISO_CODE_RESULT));
+        assertThat(geoData.get(REPRESENTED_COUNTRY_TYPE.getFieldName()), equalTo(REPRESENTED_COUNTRY_TYPE_RESULT));
+
+        assertThat(geoData.get(CITY_NAME.getFieldName()), equalTo(CITY_NAME_RESULT));
+        assertThat(geoData.get(LOCATION.getFieldName()), equalTo(LOCATION_RESULT));
+        assertThat(geoData.get(LATITUDE.getFieldName()), equalTo(LATITUDE_RESULT));
+        assertThat(geoData.get(LONGITUDE.getFieldName()), equalTo(LONGITUDE_RESULT));
+        assertThat(geoData.get(LOCATION_ACCURACY_RADIUS.getFieldName()), equalTo(LOCATION_ACCURACY_RADIUS_RESULT));
+        assertThat(geoData.get(METRO_CODE.getFieldName()), equalTo(METRO_CODE_RESULT));
+        assertThat(geoData.get(TIME_ZONE.getFieldName()), equalTo(TIME_ZONE_RESULT));
+        assertThat(geoData.get(POSTAL_CODE.getFieldName()), equalTo(POSTAL_CODE_RESULT));
+        assertThat(geoData.get(MOST_SPECIFIED_SUBDIVISION_NAME.getFieldName()), equalTo(MOST_SPECIFIED_SUBDIVISION_NAME_RESULT));
+        assertThat(geoData.get(MOST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName()), equalTo(MOST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT));
+        assertThat(geoData.get(LEAST_SPECIFIED_SUBDIVISION_NAME.getFieldName()), equalTo(LEAST_SPECIFIED_SUBDIVISION_NAME_RESULT));
+        assertThat(geoData.get(LEAST_SPECIFIED_SUBDIVISION_ISO_CODE.getFieldName()), equalTo(LEAST_SPECIFIED_SUBDIVISION_ISO_CODE_RESULT));
+    }
+
+    @Test
+    void test_getGeoData_for_all_fields_in_asn_database() throws IOException, GeoIp2Exception {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(ASN, ASN_ORGANIZATION, NETWORK, IP);
+        final Set<GeoIPDatabase> databases = Set.of(GeoIPDatabase.ASN);
+
+        when(asnDatabaseReader.tryAsn(inetAddress)).thenReturn(Optional.of(asnResponse));
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.size(), equalTo(fields.size()));
+        assertThat(geoData.get(ASN.getFieldName()), equalTo(ASN_RESULT));
+        assertThat(geoData.get(ASN_ORGANIZATION.getFieldName()), equalTo(ASN_ORG_RESULT));
+        assertThat(geoData.get(NETWORK.getFieldName()), equalTo(NETWORK_RESULT));
+        assertThat(geoData.get(IP.getFieldName()), equalTo(IP_RESULT));
+    }
+
+    @Test
+    void test_getGeoData_for_country_database_should_not_add_any_fields_if_country_is_not_required() {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(CONTINENT_NAME, CONTINENT_CODE, COUNTRY_NAME);
+        final Set<GeoIPDatabase> databases = Set.of();
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+        assertThat(geoData.size(), equalTo(0));
+    }
+
+    @Test
+    void test_getGeoData_for_city_database_should_not_add_any_fields_if_city_is_not_required() {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(CONTINENT_NAME, CONTINENT_CODE, COUNTRY_NAME);
+        final Set<GeoIPDatabase> databases = Set.of();
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.size(), equalTo(0));
+    }
+
+    @Test
+    void test_getGeoData_for_asn_database_should_not_add_any_fields_if_asn_is_not_required() {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        final List<GeoIPField> fields = List.of(ASN, ASN_ORGANIZATION, NETWORK);
+        final Set<GeoIPDatabase> databases = Set.of();
+
+        final Map<String, Object> geoData = objectUnderTest.getGeoData(inetAddress, fields, databases);
+
+        assertThat(geoData.size(), equalTo(0));
+    }
+
+    @Test
+    void test_database_expired_should_return_false_when_expiry_date_is_in_future() {
+        final Date date = new Date(9949107436565L);
+        when(metadata.getBuildDate()).thenReturn(date);
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(false));
+    }
+
+    @Test
+    void test_database_expired_should_return_true_when_expiry_date_is_in_past() throws IOException {
+        final Date date = new Date(91911199999L);
+        when(metadata.getBuildDate()).thenReturn(date);
+        doNothing().when(geoIPFileManager).deleteFile(any());
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(true));
+        verify(countryDatabaseReader).close();
+        verify(cityDatabaseReader).close();
+        verify(asnDatabaseReader).close();
+    }
+
+    @Test
+    void test_database_close_should_not_close_the_reader_unless_close_count_is_zero() {
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        objectUnderTest.retain();
+        objectUnderTest.close();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(false));
+    }
+
+    @Test
+    void test_database_close_should_close_the_reader_when_close_count_is_zero() throws IOException {
+        doNothing().when(geoIPFileManager).deleteDirectory(any());
+        final GeoLite2DatabaseReader objectUnderTest = createObjectUnderTest();
+
+        objectUnderTest.retain();
+        objectUnderTest.close();
+        objectUnderTest.close();
+
+        assertThat(objectUnderTest.isExpired(), equalTo(false));
+        verify(countryDatabaseReader).close();
+        verify(cityDatabaseReader).close();
+        verify(asnDatabaseReader).close();
+
+    }
+
+}
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoIP2DataTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoIP2DataTest.java
deleted file mode 100644
index 5493082d08..0000000000
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoIP2DataTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.opensearch.dataprepper.plugins.processor.GeoIPProcessorConfig;
-import org.opensearch.dataprepper.plugins.processor.extension.GeoIPProcessorService;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSource;
-import org.opensearch.dataprepper.plugins.processor.extension.GeoIpServiceConfig;
-import org.opensearch.dataprepper.plugins.processor.extension.MaxMindConfig;
-
-import java.io.File;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class GetGeoIP2DataTest {
-
-    private static final String PATH = "./src/test/resources/mmdb-file/geo-enterprise";
-    private String tempFolderPath = System.getProperty("java.io.tmpdir") + File.separator + "GeoIP";
-    private static final String PREFIX_DIR = "first_database";
-    public static final int REFRESH_SCHEDULE = 10;
-    public static final String IP = "2001:4860:4860::8888";
-    @Mock
-    private GeoIPProcessorConfig geoIPProcessorConfig;
-    @Mock
-    private GeoIpServiceConfig geoIpServiceConfig;
-    @Mock
-    private MaxMindConfig maxMindConfig;
-    @Mock
-    private DBSource downloadSource;
-    private GetGeoIP2Data getGeoIP2Data;
-    private final int cacheSize = 4068;
-
-    @BeforeEach
-    void setUp() {
-        when(geoIpServiceConfig.getMaxMindConfig())
-                .thenReturn(maxMindConfig);
-        String dbPath = "./src/test/resources/mmdb-file/geo-enterprise";
-        getGeoIP2Data = new GetGeoIP2Data(dbPath, cacheSize);
-    }
-
-    @Disabled("Doesn't have valid GeoIP2-Enterprise.mmdb")
-    @Test
-    void getGeoDataTest() throws UnknownHostException {
-
-        List<String> attributes = List.of("city_name", "country_name");
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        GeoIPProcessorService.downloadReady = false;
-        Map<String, Object> geoData = getGeoIP2Data.getGeoData(inetAddress, attributes, PREFIX_DIR);
-        assertThat(geoData.get("country_iso_code"), equalTo("US"));
-        assertThat(geoData.get("ip"), equalTo("2001:4860:4860:0:0:0:0:8888"));
-    }
-
-    @Test
-    @Disabled
-    void switchDatabaseReaderTest() {
-        tempFolderPath = System.getProperty("java.io.tmpdir") + File.separator + "GeoIPMaxmind";
-        DBSource.createFolderIfNotExist(tempFolderPath);
-        getGeoIP2Data = new GetGeoIP2Data(tempFolderPath, cacheSize);
-        assertDoesNotThrow(() -> {
-            getGeoIP2Data.switchDatabaseReader();
-        });
-        assertDoesNotThrow(() -> {
-            getGeoIP2Data.closeReader();
-        });
-    }
-
-    @Test
-    @Disabled
-    void getGeoDataTest_cover_EnrichFailedException() throws UnknownHostException {
-        List<String> attributes = List.of("city_name", "country_name");
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        GeoIPProcessorService.downloadReady = false;
-        assertThrows(EnrichFailedException.class, () -> getGeoIP2Data.getGeoData(inetAddress, attributes, PREFIX_DIR));
-    }
-
-    @Test
-    @Disabled
-    void closeReaderTest() throws UnknownHostException {
-        getGeoIP2Data.closeReader();
-        List<String> attributes = List.of("city_name", "country_name");
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        assertThrows(EnrichFailedException.class, () -> getGeoIP2Data.getGeoData(inetAddress, attributes, PREFIX_DIR));
-    }
-}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoLite2DataTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoLite2DataTest.java
deleted file mode 100644
index 7a58317a5d..0000000000
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/databaseenrich/GetGeoLite2DataTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.databaseenrich;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.opensearch.dataprepper.plugins.processor.extension.GeoIPProcessorService;
-import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.DBSource;
-import org.opensearch.dataprepper.plugins.processor.extension.GeoIpServiceConfig;
-import org.opensearch.dataprepper.plugins.processor.extension.MaxMindConfig;
-
-import java.io.File;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class GetGeoLite2DataTest {
-
-    private static final String PATH = "./src/test/resources/mmdb-file/geo-lite2";
-    public static final String IP = "2a02:ec00:0:0:0:0:0:0";
-    private static final String PREFIX_DIR = "first_database";
-    private String tempFolderPath = System.getProperty("java.io.tmpdir") + File.separator + "GeoIP";
-    @Mock
-    private GeoIpServiceConfig geoIpServiceConfig;
-    @Mock
-    private MaxMindConfig maxMindConfig;
-    private GetGeoLite2Data getGeoLite2Data;
-    private final int cacheSize = 4068;
-
-    @BeforeEach
-    void setUp() {
-        when(geoIpServiceConfig.getMaxMindConfig()).thenReturn(maxMindConfig);
-        when(maxMindConfig.getCacheSize()).thenReturn(cacheSize);
-
-        final String dbPath = "./src/test/resources/mmdb-file/geo-lite2";
-        getGeoLite2Data = new GetGeoLite2Data(dbPath, cacheSize);
-    }
-
-    @Test
-    @Disabled
-    void getGeoDataTest_without_attributes() throws UnknownHostException {
-        List<String> attributes = List.of();
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        GeoIPProcessorService.downloadReady = false;
-        Map<String, Object> geoData = getGeoLite2Data.getGeoData(inetAddress, attributes, tempFolderPath);
-        Assertions.assertNotNull(geoData);
-        assertThat(geoData.get("country_iso_code"), equalTo("FR"));
-        assertThat(geoData.get("ip"), equalTo(IP));
-        assertDoesNotThrow(() -> {
-            getGeoLite2Data.closeReader();
-        });
-    }
-
-    @Test
-    @Disabled
-    void getGeoDataTest_with_attributes() throws UnknownHostException {
-        List<String> attributes = List.of("city_name",
-                "country_name",
-                "ip",
-                "country_iso_code",
-                "continent_name",
-                "region_iso_code",
-                "region_name",
-                "timezone",
-                "location",
-                "asn",
-                "organization_name", "network");
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        GeoIPProcessorService.downloadReady = false;
-        Map<String, Object> geoData = getGeoLite2Data.getGeoData(inetAddress, attributes, tempFolderPath);
-        Assertions.assertNotNull(geoData);
-        assertThat(geoData.get("country_name"), equalTo("France"));
-        assertThat(geoData.get("ip"), equalTo(IP));
-        assertDoesNotThrow(() -> {
-            getGeoLite2Data.closeReader();
-        });
-    }
-
-    @Test
-    @Disabled
-    void getGeoDataTest_cover_EnrichFailedException() throws UnknownHostException {
-        List<String> attributes = List.of();
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        String dbPath = "/src/test/resources/mmdb-file/geo-enterprise";
-        getGeoLite2Data = new GetGeoLite2Data(dbPath, cacheSize);
-        GeoIPProcessorService.downloadReady = false;
-        assertThrows(EnrichFailedException.class, () -> getGeoLite2Data.getGeoData(inetAddress, attributes, tempFolderPath));
-    }
-
-    @Test
-    @Disabled
-    void switchDatabaseReaderTest() {
-        tempFolderPath = System.getProperty("java.io.tmpdir") + File.separator + "GeoIPMaxmind";
-        DBSource.createFolderIfNotExist(tempFolderPath);
-        getGeoLite2Data = new GetGeoLite2Data(tempFolderPath, cacheSize);
-        assertDoesNotThrow(() -> {
-            getGeoLite2Data.switchDatabaseReader();
-        });
-        assertDoesNotThrow(() -> {
-            getGeoLite2Data.closeReader();
-        });
-    }
-
-    @Test
-    @Disabled
-    void closeReaderTest() throws UnknownHostException {
-        // While closing the readers, all the readers reset to null.
-        getGeoLite2Data.closeReader();
-        List<String> attributes = List.of("city_name", "country_name");
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        assertThrows(NullPointerException.class, () -> getGeoLite2Data.getGeoData(inetAddress, attributes, PREFIX_DIR));
-    }
-}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/DatabaseReaderInitializationExceptionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/DatabaseReaderInitializationExceptionTest.java
new file mode 100644
index 0000000000..d5a3edbabc
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/DatabaseReaderInitializationExceptionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class DatabaseReaderInitializationExceptionTest {
+    private String message;
+
+    @BeforeEach
+    void setUp() {
+        message = UUID.randomUUID().toString();
+    }
+
+    private DatabaseReaderInitializationException createObjectUnderTest() {
+        return new DatabaseReaderInitializationException(message);
+    }
+
+    @Test
+    void getMessage_returns_message() {
+        assertThat(createObjectUnderTest().getMessage(), equalTo(message));
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/DownloadFailedExceptionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/DownloadFailedExceptionTest.java
new file mode 100644
index 0000000000..ca12b8a8d0
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/DownloadFailedExceptionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class DownloadFailedExceptionTest {
+    private String message;
+
+    @BeforeEach
+    void setUp() {
+        message = UUID.randomUUID().toString();
+    }
+
+    private DownloadFailedException createObjectUnderTest() {
+        return new DownloadFailedException(message);
+    }
+
+    @Test
+    void getMessage_returns_message() {
+        assertThat(createObjectUnderTest().getMessage(), equalTo(message));
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/EngineFailureExceptionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/EngineFailureExceptionTest.java
new file mode 100644
index 0000000000..b1a6f4b822
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/EngineFailureExceptionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class EngineFailureExceptionTest {
+    private String message;
+    @BeforeEach
+    void setUp() {
+        message = UUID.randomUUID().toString();
+    }
+
+    private EngineFailureException createObjectUnderTest() {
+        return new EngineFailureException(message);
+    }
+
+    @Test
+    void getMessage_returns_message() {
+        assertThat(createObjectUnderTest().getMessage(), equalTo(message));
+    }
+
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/EnrichFailedExceptionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/EnrichFailedExceptionTest.java
new file mode 100644
index 0000000000..af31a9532e
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/EnrichFailedExceptionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class EnrichFailedExceptionTest {
+    private String message;
+
+    @BeforeEach
+    void setUp() {
+        message = UUID.randomUUID().toString();
+    }
+
+    private EnrichFailedException createObjectUnderTest() {
+        return new EnrichFailedException(message);
+    }
+
+    @Test
+    void getMessage_returns_message() {
+        assertThat(createObjectUnderTest().getMessage(), equalTo(message));
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/InvalidIPAddressExceptionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/InvalidIPAddressExceptionTest.java
new file mode 100644
index 0000000000..d1f9e0c517
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/InvalidIPAddressExceptionTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class InvalidIPAddressExceptionTest {
+    private String message;
+    @BeforeEach
+    void setUp() {
+        message = UUID.randomUUID().toString();
+    }
+
+    private InvalidIPAddressException createObjectUnderTest() {
+        return new InvalidIPAddressException(message);
+    }
+
+    @Test
+    void getMessage_returns_message() {
+        assertThat(createObjectUnderTest().getMessage(), equalTo(message));
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/NoValidDatabaseFoundExceptionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/NoValidDatabaseFoundExceptionTest.java
new file mode 100644
index 0000000000..4463d10141
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/exception/NoValidDatabaseFoundExceptionTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.exception;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class NoValidDatabaseFoundExceptionTest {
+    private String message;
+
+    @BeforeEach
+    void setUp() {
+        message = UUID.randomUUID().toString();
+    }
+
+    private NoValidDatabaseFoundException createObjectUnderTest() {
+        return new NoValidDatabaseFoundException(message);
+    }
+
+    @Test
+    void getMessage_returns_message() {
+        assertThat(createObjectUnderTest().getMessage(), equalTo(message));
+    }
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplierTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplierTest.java
index 9d42f5b6f2..c5f484a769 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplierTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/DefaultGeoIpConfigSupplierTest.java
@@ -10,10 +10,15 @@
 import org.mockito.Mock;
 import org.mockito.MockedConstruction;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPDatabaseManager;
+
+import java.util.Optional;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mockConstruction;
 
 @ExtendWith(MockitoExtension.class)
@@ -21,8 +26,14 @@ class DefaultGeoIpConfigSupplierTest {
     @Mock
     private GeoIpServiceConfig geoIpServiceConfig;
 
+    @Mock
+    private GeoIPDatabaseManager geoIPDatabaseManager;
+
+    @Mock
+    private ReentrantReadWriteLock.ReadLock readLock;
+
     private DefaultGeoIpConfigSupplier createObjectUnderTest() {
-        return new DefaultGeoIpConfigSupplier(geoIpServiceConfig);
+        return new DefaultGeoIpConfigSupplier(geoIpServiceConfig, geoIPDatabaseManager, readLock);
     }
 
     @Test
@@ -30,11 +41,24 @@ void getGeoIpProcessorService_returns_geoIPProcessorService() {
         try (final MockedConstruction<GeoIPProcessorService> mockedConstruction =
                 mockConstruction(GeoIPProcessorService.class)) {
             final DefaultGeoIpConfigSupplier objectUnderTest = createObjectUnderTest();
-            final GeoIPProcessorService geoIPProcessorService = objectUnderTest.getGeoIPProcessorService();
+            final Optional<GeoIPProcessorService> geoIPProcessorService = objectUnderTest.getGeoIPProcessorService();
 
             assertThat(mockedConstruction.constructed().size(), equalTo(1));
-            assertThat(geoIPProcessorService, instanceOf(GeoIPProcessorService.class));
-            assertThat(geoIPProcessorService, equalTo(mockedConstruction.constructed().get(0)));
+            assertTrue(geoIPProcessorService.isPresent());
+            assertThat(geoIPProcessorService.get(), instanceOf(GeoIPProcessorService.class));
+            assertThat(geoIPProcessorService.get(), equalTo(mockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void getGeoIpProcessorService_returns_empty_if_service_config_is_null() {
+        try (final MockedConstruction<GeoIPProcessorService> mockedConstruction =
+                     mockConstruction(GeoIPProcessorService.class)) {
+            final DefaultGeoIpConfigSupplier objectUnderTest = new DefaultGeoIpConfigSupplier(null, geoIPDatabaseManager, readLock);
+            final Optional<GeoIPProcessorService> geoIPProcessorService = objectUnderTest.getGeoIPProcessorService();
+
+            assertThat(mockedConstruction.constructed().size(), equalTo(0));
+            assertTrue(geoIPProcessorService.isEmpty());
         }
     }
 }
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorServiceTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorServiceTest.java
index 232f98edec..db607a6fb0 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorServiceTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIPProcessorServiceTest.java
@@ -6,89 +6,101 @@
 package org.opensearch.dataprepper.plugins.processor.extension;
 
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.opensearch.dataprepper.plugins.processor.GeoIPProcessorConfig;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoData;
-import org.opensearch.dataprepper.test.helper.ReflectivelySetField;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.extension.databasedownload.GeoIPDatabaseManager;
 
-import java.io.File;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.time.Duration;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
-@Disabled
 class GeoIPProcessorServiceTest {
-
-    private static final String S3_URL = "https://mybucket10012023.s3.amazonaws.com/GeoLite2/";
-    private static final String URL = "https://download.maxmind.com/app/geoip_download?edition_" +
-            "id=GeoLite2-ASN&suffix=tar.gz";
-    private static final String PATH = "./src/test/resources/mmdb-file/geo-lite2";
-    private static final String S3_REGION = "us-east-1";
-    public static final int REFRESH_SCHEDULE = 10;
-    public static final String IP = "2001:4860:4860::8888";
-    String tempFolderPath = System.getProperty("java.io.tmpdir") + File.separator + "GeoIP";
     @Mock
-    private GeoIPProcessorConfig geoIPProcessorConfig;
+    private GeoIPDatabaseReader geoIPDatabaseReaderMock;
     @Mock
-    private GetGeoData geoData;
+    private GeoIPDatabaseReader newGeoIPDatabaseReaderMock;
     @Mock
     private MaxMindConfig maxMindConfig;
-    private GeoIPProcessorService geoIPProcessorService;
+    @Mock
+    private GeoIpServiceConfig geoIpServiceConfig;
+    @Mock
+    private GeoIPDatabaseManager geoIPDatabaseManager;
+    @Mock
+    private ReentrantReadWriteLock.ReadLock readLock;
 
     @BeforeEach
     void setUp() {
+        when(geoIpServiceConfig.getMaxMindConfig()).thenReturn(maxMindConfig);
+        doNothing().when(geoIPDatabaseManager).initiateDatabaseDownload();
+        when(geoIPDatabaseManager.getGeoIPDatabaseReader()).thenReturn(geoIPDatabaseReaderMock, newGeoIPDatabaseReaderMock);
+    }
 
+    GeoIPProcessorService createObjectUnderTest() {
+        return new GeoIPProcessorService(geoIpServiceConfig, geoIPDatabaseManager, readLock);
     }
 
     @Test
-    void getGeoDataTest_PATH() throws UnknownHostException, NoSuchFieldException, IllegalAccessException {
-        // TODO: pass in geoIpServiceConfig object
-        geoIPProcessorService = new GeoIPProcessorService(null);
-
-        List<String> attributes = List.of();
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        ReflectivelySetField.setField(GeoIPProcessorService.class,
-                geoIPProcessorService, "geoData", geoData);
-        when(geoData.getGeoData(any(), any(), anyString())).thenReturn(prepareGeoData());
-        Map<String, Object> geoData = geoIPProcessorService.getGeoData(inetAddress, attributes);
-        assertThat(geoData.get("country_iso_code"), equalTo("US"));
-        assertThat(geoData.get("continent_name"), equalTo("North America"));
+    void test_getGeoIPDatabaseReader_should_not_trigger_update_when_refresh_interval_is_high() {
+            when(maxMindConfig.getDatabaseRefreshInterval()).thenReturn(Duration.ofHours(1));
+
+            final GeoIPProcessorService objectUnderTest = createObjectUnderTest();
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReaderMock, equalTo(geoIPDatabaseReader));
+            verify(geoIPDatabaseManager).getGeoIPDatabaseReader();
+            verifyNoMoreInteractions(geoIPDatabaseManager);
     }
 
     @Test
-    void getGeoDataTest_URL() throws UnknownHostException, NoSuchFieldException, IllegalAccessException {
-        // TODO: pass in geoIpServiceConfig object
-        geoIPProcessorService = new GeoIPProcessorService(null);
-
-        List<String> attributes = List.of();
-        InetAddress inetAddress = InetAddress.getByName(IP);
-        ReflectivelySetField.setField(GeoIPProcessorService.class,
-                geoIPProcessorService, "geoData", geoData);
-        when(geoData.getGeoData(any(), any(), anyString())).thenReturn(prepareGeoData());
-        Map<String, Object> geoData = geoIPProcessorService.getGeoData(inetAddress, attributes);
-        assertThat(geoData.get("country_iso_code"), equalTo("US"));
-        assertThat(geoData.get("continent_name"), equalTo("North America"));
+    void test_getGeoIPDatabaseReader_should_trigger_update_when_refresh_interval_is_met() throws InterruptedException {
+        when(maxMindConfig.getDatabaseRefreshInterval()).thenReturn(Duration.ofNanos(1));
+        doNothing().when(geoIPDatabaseManager).updateDatabaseReader();
+
+        final GeoIPProcessorService objectUnderTest = createObjectUnderTest();
+
+        final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+        assertThat(geoIPDatabaseReaderMock, equalTo(geoIPDatabaseReader));
+
+        // Wait for update to be called by ExecutorService
+        Thread.sleep(1000);
+        verify(geoIPDatabaseManager).updateDatabaseReader();
+        verify(geoIPDatabaseManager).getGeoIPDatabaseReader();
+        verifyNoMoreInteractions(geoIPDatabaseManager);
+
+        final GeoIPDatabaseReader newGeoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+        assertThat(newGeoIPDatabaseReaderMock, equalTo(newGeoIPDatabaseReader));
     }
 
-    private Map<String, Object> prepareGeoData() {
-        Map<String, Object> geoDataMap = new HashMap<>();
-        geoDataMap.put("country_iso_code", "US");
-        geoDataMap.put("continent_name", "North America");
-        geoDataMap.put("timezone", "America/Chicago");
-        geoDataMap.put("country_name", "United States");
-        return geoDataMap;
+    @Test
+    void test_shutdown() throws InterruptedException {
+        when(maxMindConfig.getDatabaseRefreshInterval()).thenReturn(Duration.ofNanos(1));
+        doNothing().when(geoIPDatabaseManager).updateDatabaseReader();
+
+        final GeoIPProcessorService objectUnderTest = createObjectUnderTest();
+
+        final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+        assertThat(geoIPDatabaseReaderMock, equalTo(geoIPDatabaseReader));
+
+        // Wait for update to be called by ExecutorService
+        Thread.sleep(1000);
+        verify(geoIPDatabaseManager).updateDatabaseReader();
+        verify(geoIPDatabaseManager).getGeoIPDatabaseReader();
+        verifyNoMoreInteractions(geoIPDatabaseManager);
+
+        final GeoIPDatabaseReader newGeoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+        assertThat(newGeoIPDatabaseReaderMock, equalTo(newGeoIPDatabaseReader));
+
+        assertDoesNotThrow(objectUnderTest::shutdown);
     }
-}
\ No newline at end of file
+}
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtensionTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtensionTest.java
index 91c48322a2..78a3076fb2 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtensionTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/GeoIpConfigExtensionTest.java
@@ -5,6 +5,7 @@
 
 package org.opensearch.dataprepper.plugins.processor.extension;
 
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.ArgumentCaptor;
@@ -19,17 +20,22 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
 class GeoIpConfigExtensionTest {
     @Mock
     private ExtensionPoints extensionPoints;
-
     @Mock
     private GeoIpServiceConfig geoIpServiceConfig;
-
     @Mock
-    private ExtensionProvider.Context context;
+    private MaxMindConfig maxMindConfig;
+
+
+    @BeforeEach
+    void setUp() {
+        when(geoIpServiceConfig.getMaxMindConfig()).thenReturn(maxMindConfig);
+    }
 
     private GeoIpConfigExtension createObjectUnderTest() {
         return new GeoIpConfigExtension(geoIpServiceConfig);
@@ -48,17 +54,6 @@ void apply_should_addExtensionProvider() {
         assertThat(actualExtensionProvider, instanceOf(GeoIpConfigProvider.class));
     }
 
-    @Test
-    void extension_should_create_supplier_with_default_config_if_not_configured() {
-        try (final MockedConstruction<GeoIpServiceConfig> mockedConstruction =
-                     mockConstruction(GeoIpServiceConfig.class)) {
-            final GeoIpConfigExtension geoIpConfigExtension = new GeoIpConfigExtension(null);
-
-            assertThat(mockedConstruction.constructed().size(), equalTo(1));
-            assertThat(geoIpConfigExtension, instanceOf(GeoIpConfigExtension.class));
-        }
-    }
-
     @Test
     void extension_should_create_supplier_with_provided_config() {
         try (final MockedConstruction<GeoIpServiceConfig> mockedConstruction =
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfigTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfigTest.java
index 6b22d14bf4..228bfa75ad 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfigTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/MaxMindConfigTest.java
@@ -7,8 +7,11 @@
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.opensearch.dataprepper.test.helper.ReflectivelySetField;
 
+import java.net.URISyntaxException;
 import java.time.Duration;
 import java.util.List;
 
@@ -46,4 +49,18 @@ void testCustomConfig() throws NoSuchFieldException, IllegalAccessException {
         assertThat(maxMindConfig.getAwsAuthenticationOptionsConfig(), equalTo(awsAuthenticationOptionsConfig));
     }
 
+    @ParameterizedTest
+    @CsvSource({
+            "https://example.com/, false, true",
+            "http://example.com/, false, false",
+            "https://example.com/, true, true",
+            "http://example.com/, true, true"})
+    void testSecureEndpoint(final String databasePath, final boolean insecure, final boolean result)
+            throws NoSuchFieldException, IllegalAccessException, URISyntaxException {
+        ReflectivelySetField.setField(MaxMindConfig.class, maxMindConfig, "databasePaths", List.of(databasePath));
+        ReflectivelySetField.setField(MaxMindConfig.class, maxMindConfig, "insecure", insecure);
+
+        assertThat(maxMindConfig.getDatabasePaths().size(), equalTo(1));
+        assertThat(maxMindConfig.isHttpsEndpointOrInsecure(), equalTo(result));
+    }
 }
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceTest.java
deleted file mode 100644
index 3b7ed49d87..0000000000
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DBSourceTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.MockedStatic;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.File;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.mockStatic;
-
-@ExtendWith(MockitoExtension.class)
-class DBSourceTest {
-
-    private final String outputFilePath = System.getProperty("java.io.tmpdir") + "GeoTest";
-
-    @Test
-    void createFolderIfNotExistTest() {
-        try (MockedStatic<DBSource> mockedStatic = mockStatic(DBSource.class)) {
-            mockedStatic.when(() -> DBSource.createFolderIfNotExist(outputFilePath)).thenReturn(new File(outputFilePath));
-            File actualFile = new File(outputFilePath);
-            assertEquals(actualFile, DBSource.createFolderIfNotExist(outputFilePath));
-        }
-    }
-
-    @Test
-    void deleteDirectoryTest() {
-        DBSource.createFolderIfNotExist(outputFilePath);
-        DBSource.createFolderIfNotExist(outputFilePath + File.separator + "GeoIPz");
-        DBSource.createFolderIfNotExist(outputFilePath + File.separator + "GeoIPx");
-        assertDoesNotThrow(() -> {
-            DBSource.deleteDirectory(new File(outputFilePath));
-        });
-    }
-}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderBuilderTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderBuilderTest.java
new file mode 100644
index 0000000000..1b435fb1f2
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderBuilderTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import com.maxmind.geoip2.DatabaseReader;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(MockitoExtension.class)
+class DatabaseReaderBuilderTest {
+    public static final String GEOLITE2_TEST_MMDB_FILES = "./build/resources/test/mmdb-files/geo-lite2";
+    public static final String GEOIP2_TEST_MMDB_FILES = "./build/resources/test/mmdb-files/geo-ip2";
+
+    @ParameterizedTest
+    @ValueSource(strings = {"geolite2-asn", "geolite2-city", "geolite2-country"})
+    void createLoaderTest_for_geolite2_databases(final String databaseName) throws IOException {
+        String databaseToUse = null;
+        final File directory = new File(GEOLITE2_TEST_MMDB_FILES);
+        final String[] list = directory.list();
+        for (String fileName: list) {
+            final String lowerCaseFileName = fileName.toLowerCase();
+            if (fileName.endsWith(".mmdb")
+                    && lowerCaseFileName.contains(databaseName)) {
+                databaseToUse = fileName;
+            }
+        }
+
+        final Path path = Path.of(GEOLITE2_TEST_MMDB_FILES + File.separator + databaseToUse);
+
+        final DatabaseReaderBuilder databaseReaderBuilder = new DatabaseReaderBuilder();
+
+        final DatabaseReader databaseReader = databaseReaderBuilder.buildReader(path, 4096);
+        assertNotNull(databaseReader);
+        assertTrue(databaseToUse.toLowerCase().contains(databaseReader.getMetadata().getDatabaseType().toLowerCase()));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"geoip2-enterprise"})
+    void createLoaderTest_for_geoip2_databases(final String databaseName) throws IOException {
+        String databaseToUse = null;
+        final File directory = new File(GEOIP2_TEST_MMDB_FILES);
+        final String[] list = directory.list();
+        for (String fileName: list) {
+            final String lowerCaseFileName = fileName.toLowerCase();
+            if (fileName.endsWith(".mmdb")
+                    && lowerCaseFileName.contains(databaseName)) {
+                databaseToUse = fileName;
+            }
+        }
+
+        final Path path = Path.of(GEOIP2_TEST_MMDB_FILES + File.separator + databaseToUse);
+
+        final DatabaseReaderBuilder databaseReaderBuilder = new DatabaseReaderBuilder();
+
+        final DatabaseReader databaseReader = databaseReaderBuilder.buildReader(path, 4096);
+        assertNotNull(databaseReader);
+        assertTrue(databaseToUse.toLowerCase().contains(databaseReader.getMetadata().getDatabaseType().toLowerCase()));
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderCreateTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderCreateTest.java
deleted file mode 100644
index 621498e8e2..0000000000
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/DatabaseReaderCreateTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
-
-import com.maxmind.geoip2.DatabaseReader;
-import org.apache.commons.io.FileUtils;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.nio.file.Path;
-
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class DatabaseReaderCreateTest {
-    @Mock
-    private Path path;
-
-    @Test
-    void createLoaderTest() throws IOException {
-        final String testFileURL = "https://github.com/maxmind/MaxMind-DB/raw/main/test-data/GeoLite2-City-Test.mmdb";
-        final File file = File.createTempFile( "GeoIP2-City-Test", ".mmdb");
-
-        final BufferedInputStream in = new BufferedInputStream(new URL(testFileURL).openStream());
-        FileUtils.copyInputStreamToFile(in, file);
-        when(path.toFile()).thenReturn(file);
-
-        DatabaseReader databaseReader = DatabaseReaderCreate.createLoader(path, 4096);
-        Assertions.assertNotNull(databaseReader);
-        in.close();
-        file.deleteOnExit();
-    }
-}
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoDataFactoryTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoDataFactoryTest.java
deleted file mode 100644
index 83be77f774..0000000000
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoDataFactoryTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoData;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoIP2Data;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.GetGeoLite2Data;
-import org.opensearch.dataprepper.plugins.processor.extension.MaxMindConfig;
-import org.opensearch.dataprepper.plugins.processor.utils.LicenseTypeCheck;
-
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class GeoDataFactoryTest {
-
-    @Mock
-    private MaxMindConfig maxMindConfig;
-
-    @Mock
-    private LicenseTypeCheck licenseTypeCheck;
-
-    @Test
-    void testCreateWithFreeLicense() {
-        when(licenseTypeCheck.isGeoLite2OrEnterpriseLicense(anyString())).thenReturn(LicenseTypeOptions.FREE);
-        final GeoDataFactory geoDataFactory = new GeoDataFactory(maxMindConfig, licenseTypeCheck);
-        final String databasePath = "testPath";
-
-        final GetGeoData getGeoData = geoDataFactory.create(databasePath);
-        assertInstanceOf(GetGeoLite2Data.class, getGeoData);
-    }
-
-    @Test
-    void testCreateWithEnterpriseLicense() {
-        when(licenseTypeCheck.isGeoLite2OrEnterpriseLicense(anyString())).thenReturn(LicenseTypeOptions.ENTERPRISE);
-        final GeoDataFactory geoDataFactory = new GeoDataFactory(maxMindConfig, licenseTypeCheck);
-        final String databasePath = "testPath";
-
-        final GetGeoData getGeoData = geoDataFactory.create(databasePath);
-        assertInstanceOf(GetGeoIP2Data.class, getGeoData);
-    }
-
-}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPDatabaseManagerTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPDatabaseManagerTest.java
new file mode 100644
index 0000000000..f19a3ccc82
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPDatabaseManagerTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedConstruction;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIP2DatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoIPDatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.databaseenrich.GeoLite2DatabaseReader;
+import org.opensearch.dataprepper.plugins.processor.extension.MaxMindConfig;
+import org.opensearch.dataprepper.plugins.processor.utils.LicenseTypeCheck;
+
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class GeoIPDatabaseManagerTest {
+    private static final String S3_URI = "s3://geoip/data";
+    private static final String HTTPS_URL = "https://download.maxmind.com/app/geoip_download?editionid=GeoLite2-ASN&suffix=tar.gz";
+    private static final String PATH = "./build/resources/test/mmdb-files/geo-lite2";
+    private static final String CDN_ENDPOINT = "https://devo.geoip.maps.opensearch.org/v1/mmdb/geolite2/manifest.json";
+
+    @Mock
+    private MaxMindConfig maxMindConfig;
+
+    @Mock
+    private LicenseTypeCheck licenseTypeCheck;
+
+    @Mock
+    private DatabaseReaderBuilder databaseReaderBuilder;
+
+    @Mock
+    private ReentrantReadWriteLock.WriteLock writeLock;
+
+    @Mock
+    private GeoIPFileManager databaseFileManager;
+
+    @BeforeEach
+    void setUp() {
+        when(licenseTypeCheck.isGeoLite2OrEnterpriseLicense(any())).thenReturn(LicenseTypeOptions.FREE);
+    }
+
+    private GeoIPDatabaseManager createObjectUnderTest() {
+        return new GeoIPDatabaseManager(maxMindConfig, licenseTypeCheck, databaseReaderBuilder, databaseFileManager, writeLock);
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geolite2_file_path_should_use_local_download_service_and_geolite2_reader() throws Exception {
+        try (final MockedConstruction<LocalDBDownloadService> localDBDownloadServiceMockedConstruction = mockConstruction(LocalDBDownloadService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(PATH)));
+             final MockedConstruction<GeoLite2DatabaseReader> geoLite2DatabaseReaderMockedConstruction = mockConstruction(GeoLite2DatabaseReader.class)
+        ) {
+
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(PATH));
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(localDBDownloadServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(localDBDownloadServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(PATH));
+            assertThat(geoLite2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoLite2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geoip2_file_path_should_use_local_download_service_and_geoip2_reader() throws Exception {
+        try (final MockedConstruction<LocalDBDownloadService> localDBDownloadServiceMockedConstruction = mockConstruction(LocalDBDownloadService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(PATH)));
+             final MockedConstruction<GeoIP2DatabaseReader> geoIP2DatabaseReaderMockedConstruction = mockConstruction(GeoIP2DatabaseReader.class)
+        ) {
+            when(licenseTypeCheck.isGeoLite2OrEnterpriseLicense(any())).thenReturn(LicenseTypeOptions.ENTERPRISE);
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(PATH));
+
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(localDBDownloadServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(localDBDownloadServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(PATH));
+            assertThat(geoIP2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoIP2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geolite2_s3_uri_should_use_s3_download_service_and_geolite2_reader() {
+        try (final MockedConstruction<S3DBService> s3DBServiceMockedConstruction = mockConstruction(S3DBService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(S3_URI)));
+             final MockedConstruction<GeoLite2DatabaseReader> geoLite2DatabaseReaderMockedConstruction = mockConstruction(GeoLite2DatabaseReader.class)
+        ) {
+
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(S3_URI));
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(s3DBServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(s3DBServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(S3_URI));
+            assertThat(geoLite2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoLite2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geoip2_s3_uri_should_use_s3_download_service_and_geoip2_reader() {
+        try (final MockedConstruction<S3DBService> s3DBServiceMockedConstruction = mockConstruction(S3DBService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(S3_URI)));
+             final MockedConstruction<GeoIP2DatabaseReader> geoIP2DatabaseReaderMockedConstruction = mockConstruction(GeoIP2DatabaseReader.class)
+        ) {
+            when(licenseTypeCheck.isGeoLite2OrEnterpriseLicense(any())).thenReturn(LicenseTypeOptions.ENTERPRISE);
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(S3_URI));
+
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(s3DBServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(s3DBServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(S3_URI));
+            assertThat(geoIP2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoIP2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geolite2_url_should_use_http_download_service_and_geolite2_reader() {
+        try (final MockedConstruction<HttpDBDownloadService> httpDBDownloadServiceMockedConstruction = mockConstruction(HttpDBDownloadService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(HTTPS_URL)));
+            final MockedConstruction<GeoLite2DatabaseReader> geoLite2DatabaseReaderMockedConstruction = mockConstruction(GeoLite2DatabaseReader.class)
+        ) {
+
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(HTTPS_URL));
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(httpDBDownloadServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(httpDBDownloadServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(HTTPS_URL));
+            assertThat(geoLite2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoLite2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geoip2_url_should_use_http_download_service_and_geoip2_reader() {
+        try (final MockedConstruction<HttpDBDownloadService> httpDBDownloadServiceMockedConstruction = mockConstruction(HttpDBDownloadService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(HTTPS_URL)));
+             final MockedConstruction<GeoIP2DatabaseReader> geoIP2DatabaseReaderMockedConstruction = mockConstruction(GeoIP2DatabaseReader.class)
+        ) {
+            when(licenseTypeCheck.isGeoLite2OrEnterpriseLicense(any())).thenReturn(LicenseTypeOptions.ENTERPRISE);
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(HTTPS_URL));
+
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(httpDBDownloadServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(httpDBDownloadServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(HTTPS_URL));
+            assertThat(geoIP2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoIP2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_initiateDatabaseDownload_with_geolite2_cdn_should_use_cdn_download_service_and_geolite2_reader() {
+        try (final MockedConstruction<ManifestDownloadService> cdnDownloadServiceMockedConstruction = mockConstruction(ManifestDownloadService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(CDN_ENDPOINT)));
+             final MockedConstruction<GeoLite2DatabaseReader> geoLite2DatabaseReaderMockedConstruction = mockConstruction(GeoLite2DatabaseReader.class)
+        ) {
+
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(CDN_ENDPOINT));
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+            objectUnderTest.initiateDatabaseDownload();
+
+            assertThat(cdnDownloadServiceMockedConstruction.constructed().size(), equalTo(1));
+            verify(cdnDownloadServiceMockedConstruction.constructed().get(0)).initiateDownload(List.of(CDN_ENDPOINT));
+            assertThat(geoLite2DatabaseReaderMockedConstruction.constructed().size(), equalTo(1));
+
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoLite2DatabaseReaderMockedConstruction.constructed().get(0)));
+        }
+    }
+
+    @Test
+    void test_updateDatabaseReader_with_geolite2_cdn_should_use_cdn_download_service_and_geolite2_reader_and_get_new_reader() {
+        try (final MockedConstruction<ManifestDownloadService> cdnDownloadServiceMockedConstruction = mockConstruction(ManifestDownloadService.class,
+                     (mock2, context2)-> doNothing().when(mock2).initiateDownload(List.of(CDN_ENDPOINT)));
+             final MockedConstruction<GeoLite2DatabaseReader> geoLite2DatabaseReaderMockedConstruction = mockConstruction(GeoLite2DatabaseReader.class)
+        ) {
+            when(maxMindConfig.getDatabasePaths()).thenReturn(List.of(CDN_ENDPOINT));
+            final GeoIPDatabaseManager objectUnderTest = createObjectUnderTest();
+
+            objectUnderTest.initiateDatabaseDownload();
+            final GeoIPDatabaseReader geoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(geoIPDatabaseReader, equalTo(geoLite2DatabaseReaderMockedConstruction.constructed().get(0)));
+
+            objectUnderTest.updateDatabaseReader();
+
+            assertThat(cdnDownloadServiceMockedConstruction.constructed().size(), equalTo(2));
+            for (ManifestDownloadService manifestDownloadService : cdnDownloadServiceMockedConstruction.constructed()) {
+                verify(manifestDownloadService).initiateDownload(List.of(CDN_ENDPOINT));
+            }
+            assertThat(geoLite2DatabaseReaderMockedConstruction.constructed().size(), equalTo(2));
+            // verify if first instance is closed
+            verify(geoLite2DatabaseReaderMockedConstruction.constructed().get(0)).close();
+            final GeoIPDatabaseReader updatedGeoIPDatabaseReader = objectUnderTest.getGeoIPDatabaseReader();
+            assertThat(updatedGeoIPDatabaseReader, equalTo(geoLite2DatabaseReaderMockedConstruction.constructed().get(1)));
+        }
+    }
+}
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPFileManagerTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPFileManagerTest.java
new file mode 100644
index 0000000000..c7c74acf8d
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/GeoIPFileManagerTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class GeoIPFileManagerTest {
+    private final String outputFilePath = "./src/test/resources/geoip/test";
+
+    @Test
+    void createFolderIfNotExistTest() {
+        final GeoIPFileManager geoIPFileManager = new GeoIPFileManager();
+        geoIPFileManager.createDirectoryIfNotExist(outputFilePath);
+
+        final File file = new File(outputFilePath);
+        assertTrue(file.exists());
+    }
+
+    @Test
+    void deleteDirectoryTest() {
+        final GeoIPFileManager geoIPFileManager = new GeoIPFileManager();
+        geoIPFileManager.createDirectoryIfNotExist(outputFilePath);
+
+        final File file = new File(outputFilePath);
+        assertTrue(file.isDirectory());
+        geoIPFileManager.deleteDirectory(file);
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadServiceTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadServiceTest.java
index 8b2bbf47cd..87152004e8 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadServiceTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/HttpDBDownloadServiceTest.java
@@ -7,6 +7,7 @@
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.util.List;
@@ -18,6 +19,8 @@ class HttpDBDownloadServiceTest {
 
     private static final String PREFIX_DIR = "first_database";
     private HttpDBDownloadService downloadThroughUrl;
+    @Mock
+    private GeoIPFileManager geoIPFileManager;
 
     @Test
     void initiateDownloadTest() {
@@ -30,6 +33,6 @@ void initiateDownloadTest() {
     }
 
     private HttpDBDownloadService createObjectUnderTest() {
-        return new HttpDBDownloadService(PREFIX_DIR);
+        return new HttpDBDownloadService(PREFIX_DIR, geoIPFileManager);
     }
 }
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/ManifestDownloadServiceTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/ManifestDownloadServiceTest.java
new file mode 100644
index 0000000000..2dfe276104
--- /dev/null
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/ManifestDownloadServiceTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.dataprepper.plugins.processor.extension.databasedownload;
+
+import org.junit.jupiter.api.Test;
+import org.opensearch.dataprepper.plugins.processor.exception.DownloadFailedException;
+
+import java.io.File;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ManifestDownloadServiceTest {
+    private static final String OUTPUT_DIR = "./src/test/resources/geoip";
+
+    private ManifestDownloadService createObjectUnderTest() {
+        return new ManifestDownloadService(OUTPUT_DIR);
+    }
+
+    @Test
+    void test_with_valid_endpoint_should_download_file() {
+        final ManifestDownloadService objectUnderTest = createObjectUnderTest();
+        objectUnderTest.initiateDownload(List.of("https://devo.geoip.maps.opensearch.org/v1/mmdb/geolite2-city/manifest.json"));
+
+        final File file = new File(OUTPUT_DIR + File.separator + "geolite2-city.mmdb");
+        assertTrue(file.exists());
+
+        file.deleteOnExit();
+        final File directory = new File(OUTPUT_DIR);
+        directory.deleteOnExit();
+    }
+
+    @Test
+    void test_with_invalid_endpoint_should_throw_exception() {
+        final ManifestDownloadService objectUnderTest = createObjectUnderTest();
+        assertThrows(DownloadFailedException.class, () -> objectUnderTest
+                .initiateDownload(List.of("https://devo.geoip.maps.opensearch.org/v1/mmdb/geolite2-enterprise/manifest.json")));
+    }
+
+}
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBServiceTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBServiceTest.java
index 815d2eb624..42e35164e7 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBServiceTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/extension/databasedownload/S3DBServiceTest.java
@@ -11,7 +11,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.opensearch.dataprepper.plugins.processor.GeoIPProcessorConfig;
-import org.opensearch.dataprepper.plugins.processor.databaseenrich.DownloadFailedException;
+import org.opensearch.dataprepper.plugins.processor.exception.DownloadFailedException;
 
 import java.util.List;
 
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentificationTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentificationTest.java
index 7be0b00db9..64b3a8f24c 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentificationTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/DbSourceIdentificationTest.java
@@ -20,65 +20,81 @@
 class DbSourceIdentificationTest {
 
     private static final String S3_URI = "s3://dataprepper/logdata/22833bd46b8e0.mmdb";
-    private static final String S3_URL = "https://dataprepper.s3.amazonaws.com/logdata/22833bd46b8e0.json";
     private static final String URL = "https://www.dataprepper.com";
-    private static final String PATH = "./src/test/resources/mmdb-file/geo-lite2";
+    private static final String DIRECTORY_PATH = "./build/resources/test/mmdb-files/geo-lite2";
+    private static final String FILE_PATH = "./build/resources/test/mmdb-files/geo-lite2/GeoLite2-ASN-Test.mmdb";
+    private static final String CDN_ENDPOINT_HOST = "https://devo.geoip.maps.opensearch.org/v1/mmdb/geolite2/manifest.json";
 
     @Test
     void test_positive_case() {
-        assertTrue(DbSourceIdentification.isS3Uri(S3_URI));
-        assertTrue(DbSourceIdentification.isS3Url(S3_URL));
-        assertTrue(DbSourceIdentification.isURL(URL));
-        assertTrue(DbSourceIdentification.isFilePath(PATH));
+        assertTrue(DatabaseSourceIdentification.isS3Uri(S3_URI));
+        assertTrue(DatabaseSourceIdentification.isURL(URL));
+        assertTrue(DatabaseSourceIdentification.isFilePath(DIRECTORY_PATH));
+        assertTrue(DatabaseSourceIdentification.isCDNEndpoint(CDN_ENDPOINT_HOST));
     }
 
     @Test
     void test_negative_case() {
-        assertFalse(DbSourceIdentification.isS3Uri(S3_URL));
-        assertFalse(DbSourceIdentification.isS3Uri(URL));
-        assertFalse(DbSourceIdentification.isS3Uri(PATH));
+        assertFalse(DatabaseSourceIdentification.isS3Uri(CDN_ENDPOINT_HOST));
+        assertFalse(DatabaseSourceIdentification.isS3Uri(URL));
+        assertFalse(DatabaseSourceIdentification.isS3Uri(DIRECTORY_PATH));
 
-        assertFalse(DbSourceIdentification.isS3Url(S3_URI));
-        assertFalse(DbSourceIdentification.isS3Url(URL));
-        assertFalse(DbSourceIdentification.isS3Url(PATH));
+        assertFalse(DatabaseSourceIdentification.isURL(S3_URI));
+        assertFalse(DatabaseSourceIdentification.isURL(CDN_ENDPOINT_HOST));
+        assertFalse(DatabaseSourceIdentification.isURL(DIRECTORY_PATH));
 
-        assertFalse(DbSourceIdentification.isURL(S3_URI));
-        assertFalse(DbSourceIdentification.isURL(S3_URL));
-        assertFalse(DbSourceIdentification.isURL(PATH));
+        assertFalse(DatabaseSourceIdentification.isFilePath(S3_URI));
+        assertFalse(DatabaseSourceIdentification.isFilePath(CDN_ENDPOINT_HOST));
+        assertFalse(DatabaseSourceIdentification.isFilePath(URL));
 
-        assertFalse(DbSourceIdentification.isFilePath(S3_URI));
-        assertFalse(DbSourceIdentification.isFilePath(S3_URL));
-        assertFalse(DbSourceIdentification.isFilePath(URL));
+        assertFalse(DatabaseSourceIdentification.isCDNEndpoint(S3_URI));
+        assertFalse(DatabaseSourceIdentification.isCDNEndpoint(DIRECTORY_PATH));
+        assertFalse(DatabaseSourceIdentification.isCDNEndpoint(URL));
     }
 
     @Test
-    void getDatabasePathTypeTest_PATH() throws NoSuchFieldException, IllegalAccessException {
-        List<String> databasePath = List.of("./src/test/resources/mmdb-file/geo-lite2");
-        DBSourceOptions dbSourceOptions = DbSourceIdentification.getDatabasePathType(databasePath);
+    void getDatabasePathTypeTest_PATH() {
+        List<String> databasePath = List.of(DIRECTORY_PATH);
+        DBSourceOptions dbSourceOptions = DatabaseSourceIdentification.getDatabasePathType(databasePath);
         Assertions.assertNotNull(dbSourceOptions);
         assertThat(dbSourceOptions, equalTo(DBSourceOptions.PATH));
     }
 
     @Test
-    void getDatabasePathTypeTest_URL() throws NoSuchFieldException, IllegalAccessException {
+    void getDatabasePathTypeTest_should_return_null_if_not_directory() {
+        List<String> databasePath = List.of(FILE_PATH);
+        DBSourceOptions dbSourceOptions = DatabaseSourceIdentification.getDatabasePathType(databasePath);
+        Assertions.assertNull(dbSourceOptions);
+    }
+
+    @Test
+    void getDatabasePathTypeTest_URL() {
         List<String> databasePath = List.of("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&suffix=tar.gz");
-        DBSourceOptions dbSourceOptions = DbSourceIdentification.getDatabasePathType(databasePath);
+        DBSourceOptions dbSourceOptions = DatabaseSourceIdentification.getDatabasePathType(databasePath);
         Assertions.assertNotNull(dbSourceOptions);
         assertThat(dbSourceOptions, equalTo(DBSourceOptions.URL));
     }
 
     @Test
-    void getDatabasePathTypeTest_S3() throws NoSuchFieldException, IllegalAccessException {
+    void getDatabasePathTypeTest_S3() {
         List<String> databasePath = List.of("s3://mybucket10012023/GeoLite2/");
-        DBSourceOptions dbSourceOptions = DbSourceIdentification.getDatabasePathType(databasePath);
+        DBSourceOptions dbSourceOptions = DatabaseSourceIdentification.getDatabasePathType(databasePath);
         Assertions.assertNotNull(dbSourceOptions);
         assertThat(dbSourceOptions, equalTo(DBSourceOptions.S3));
     }
 
+    @Test
+    void getDatabasePathTypeTest_CDN() {
+        List<String> databasePath = List.of(CDN_ENDPOINT_HOST);
+        DBSourceOptions dbSourceOptions = DatabaseSourceIdentification.getDatabasePathType(databasePath);
+        Assertions.assertNotNull(dbSourceOptions);
+        assertThat(dbSourceOptions, equalTo(DBSourceOptions.HTTP_MANIFEST));
+    }
+
     @Test
     void isS3Uri_NullPointerException_test() {
         assertDoesNotThrow(() -> {
-            DbSourceIdentification.isS3Uri(null);
+            DatabaseSourceIdentification.isS3Uri(null);
         });
     }
 }
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheckTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheckTest.java
index 487f131f81..03dc47aac0 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheckTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/IPValidationCheckTest.java
@@ -9,28 +9,29 @@
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.opensearch.dataprepper.plugins.processor.exception.InvalidIPAddressException;
 
-import java.net.UnknownHostException;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 @ExtendWith(MockitoExtension.class)
 class IPValidationCheckTest {
 
     private static final String PRIVATE_IP_ADDRESS = "192.168.29.233";
     private static final String PUBLIC_IP_ADDRESS = "2001:4860:4860::8888";
-    private static final String INVALID_IP_ADDRESS = "255.255.255.0";
+    private static final String INVALID_IP_ADDRESS = "255.255.255.999";
 
     @Test
-    void ipValidationcheckTest_positive() throws UnknownHostException {
+    void ipValidationcheckTest_public() {
         Assertions.assertTrue(IPValidationCheck.isPublicIpAddress(PUBLIC_IP_ADDRESS));
     }
 
     @Test
-    void ipValidationcheckTest_negative() throws UnknownHostException {
+    void ipValidationcheckTest_negative() {
         Assertions.assertFalse(IPValidationCheck.isPublicIpAddress(PRIVATE_IP_ADDRESS));
     }
 
     @Test
-    void ipValidationcheckTest_invalid() throws UnknownHostException {
-        Assertions.assertTrue(IPValidationCheck.isPublicIpAddress(INVALID_IP_ADDRESS));
+    void ipValidationcheckTest_invalid() {
+        assertThrows(InvalidIPAddressException.class, () -> IPValidationCheck.isPublicIpAddress(INVALID_IP_ADDRESS));
     }
 }
\ No newline at end of file
diff --git a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheckTest.java b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheckTest.java
index 02d40cdf1e..5530367d7c 100644
--- a/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheckTest.java
+++ b/data-prepper-plugins/geoip-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/utils/LicenseTypeCheckTest.java
@@ -17,30 +17,24 @@
 @ExtendWith(MockitoExtension.class)
 class LicenseTypeCheckTest {
 
-    private static final String FOLDER_PATH_GEO_LITE2 = "./src/test/resources/mmdb-file/geo-lite2";
-    private static final String FOLDER_PATH_GEO_ENTERPRISE = "./src/test/resources/mmdb-file/geo-enterprise";
+    private static final String FOLDER_PATH_GEO_LITE2 = "./build/resources/test/mmdb-files/geo-lite2";
+    private static final String FOLDER_PATH_GEO_ENTERPRISE = "./build/resources/test/mmdb-files/geo-enterprise";
 
     private LicenseTypeCheck createObjectUnderTest() {
         return new LicenseTypeCheck();
     }
 
     @Test
-    void isGeoLite2OrEnterpriseLicenseTest_positive() {
+    void test_isGeoLite2OrEnterpriseLicenseTest_should_return_free_when_geolite2_databases_are_used() {
         final LicenseTypeCheck objectUnderTest = createObjectUnderTest();
         LicenseTypeOptions licenseTypeOptionsFree = objectUnderTest.isGeoLite2OrEnterpriseLicense(FOLDER_PATH_GEO_LITE2);
         assertThat(licenseTypeOptionsFree, equalTo(LicenseTypeOptions.FREE));
-
-        LicenseTypeOptions licenseTypeOptionsEnterprise = objectUnderTest.isGeoLite2OrEnterpriseLicense(FOLDER_PATH_GEO_ENTERPRISE);
-        assertThat(licenseTypeOptionsEnterprise, equalTo(LicenseTypeOptions.ENTERPRISE));
     }
 
     @Test
-    void isGeoLite2OrEnterpriseLicenseTest_negative() {
+    void test_isGeoLite2OrEnterpriseLicenseTest_should_return_enterprise_when_geoip2_databases_are_used() {
         final LicenseTypeCheck objectUnderTest = createObjectUnderTest();
         LicenseTypeOptions licenseTypeOptionsFree = objectUnderTest.isGeoLite2OrEnterpriseLicense(FOLDER_PATH_GEO_ENTERPRISE);
-        assertThat(licenseTypeOptionsFree, not(equalTo(LicenseTypeOptions.FREE)));
-
-        LicenseTypeOptions licenseTypeOptionsEnterprise = objectUnderTest.isGeoLite2OrEnterpriseLicense(FOLDER_PATH_GEO_LITE2);
-        assertThat(licenseTypeOptionsEnterprise, not(equalTo(LicenseTypeOptions.ENTERPRISE)));
+        assertThat(licenseTypeOptionsFree, not(equalTo(LicenseTypeOptions.ENTERPRISE)));
     }
 }
\ No newline at end of file