LP2012402 Authoritative APIs are optional; disabled by default user/berick/lp2012402-authoritative-optional
authorBill Erickson <berickxx@gmail.com>
Tue, 21 Mar 2023 15:27:25 +0000 (11:27 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 21 Mar 2023 15:27:28 +0000 (11:27 -0400)
Adds a new setting to opensrf.xml called 'uses_pooled_read_replica_dbs'
which controls whether Evergreen enables support for "authoritative" API
calls.

When disabled, the ".authoritative" API variant is simply a clone of the
non-authoritative version.  Similarly, the Angular PCRUD service will
avoid making transaction begin/rollback calls when the 'authoritative'
PCRUD option is passed locally.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/examples/opensrf.xml.example
Open-ILS/src/eg2/src/app/core/pcrud.service.ts
Open-ILS/src/eg2/src/app/resolver.service.ts
Open-ILS/src/perlmods/lib/OpenILS/Application.pm
docs/RELEASE_NOTES_NEXT/Administration/authoritative.adoc [new file with mode: 0644]

index 05d945f..8afdb6a 100644 (file)
@@ -404,6 +404,14 @@ vim:et:ts=4:sw=4:
         <!-- no apps are enabled globally by default -->
         <activeapps/> 
 
+        <!-- 
+            If this setting is "true", Evergreen "authoritative" APIs
+            will be enabled, forcing select API calls to wrap read calls
+            in a database BEGIN/ROLLBACK so they read from the primary
+            database.
+        -->
+        <uses_pooled_read_replica_dbs>false</uses_pooled_read_replica_dbs>
+
         <cache>
             <!-- memcache servers -->
             <global>
index d95869e..b6b39ac 100644 (file)
@@ -3,6 +3,7 @@ import {Observable, Observer} from 'rxjs';
 import {IdlService, IdlObject} from './idl.service';
 import {NetService, NetRequest} from './net.service';
 import {AuthService} from './auth.service';
+import {StoreService} from './store.service';
 
 // Externally defined.  Used here for debugging.
 declare var js2JSON: (jsThing: any) => string;
@@ -202,7 +203,7 @@ export class PcrudContext {
     }
 
     private dispatch(method: string, params: any[]): Observable<PcrudResponse> {
-        if (this.authoritative) {
+        if (this.authoritative && PcrudService.useAuthoritative) {
             return this.wrapXact(() => {
                 return this.sendRequest(method, params);
             });
@@ -316,9 +317,11 @@ export class PcrudContext {
 
 @Injectable({providedIn: 'root'})
 export class PcrudService {
+    static useAuthoritative = false;
 
     constructor(
         private idl: IdlService,
+        private store: StoreService,
         private net: NetService,
         private auth: AuthService
     ) {}
@@ -363,6 +366,25 @@ export class PcrudService {
     autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
         return this.newContext().autoApply(list);
     }
+
+    setAuthoritative(): Promise<boolean> {
+
+        const enabled = this.store.getLocalItem('eg.sys.use_authoritative');
+        if (typeof enabled === 'boolean') {
+            PcrudService.useAuthoritative = enabled;
+            return Promise.resolve(enabled);
+        }
+
+        return this.net.request(
+            'open-ils.actor',
+            'opensrf.open-ils.system.use_authoritative'
+        ).toPromise().then(enabled => {
+            enabled = Boolean(Number(enabled));
+            PcrudService.useAuthoritative = enabled;
+            this.store.setLocalItem('eg.sys.use_authoritative', enabled);
+            return enabled;
+        });
+    }
 }
 
 
index faa6038..b84289f 100644 (file)
@@ -4,6 +4,7 @@ import {Router, Resolve, RouterStateSnapshot,
 import {IdlService} from '@eg/core/idl.service';
 import {OrgService} from '@eg/core/org.service';
 import {LocaleService} from '@eg/core/locale.service';
+import {PcrudService} from '@eg/core/pcrud.service';
 
 // For locale application
 declare var OpenSRF;
@@ -15,6 +16,7 @@ export class BaseResolver implements Resolve<Promise<void>> {
         private router: Router,
         private idl: IdlService,
         private org: OrgService,
+        private pcrud: PcrudService,
         private locale: LocaleService
     ) {}
 
@@ -31,6 +33,10 @@ export class BaseResolver implements Resolve<Promise<void>> {
 
         this.idl.parseIdl();
 
-        return this.org.fetchOrgs(); // anonymous PCRUD.
+        return this.pcrud.setAuthoritative()
+        .then(enabled => {
+            console.debug('Authoritative API support enabled=' + enabled);
+            return this.org.fetchOrgs();
+        });
     }
 }
index d750216..1c09bee 100644 (file)
@@ -4,6 +4,25 @@ use UNIVERSAL::require;
 use base qw/OpenSRF::Application/;
 use OpenILS::Utils::Fieldmapper;
 
+__PACKAGE__->register_method(
+    api_name    => 'opensrf.open-ils.system.use_authoritative',
+    api_level   => 1,
+    method      => 'use_authoritative',
+);
+
+# Do authoritative methods do anything different or are they simply
+# clones of their non-authoritative variant?
+my $_use_authoritative;
+sub use_authoritative {
+    if (!defined $_use_authoritative) {
+        my $ua = OpenSRF::Utils::SettingsClient
+            ->new->config_value('uses_pooled_read_replica_dbs') || '';
+        $_use_authoritative = lc($ua) eq 'true';
+    }
+
+    return $_use_authoritative;
+}
+
 sub ils_version {
     # version format is "x-y-z", for example "2-0-0" for Evergreen 2.0.0
     # For branches, format is "x-y"
@@ -74,7 +93,7 @@ sub authoritative_wrapper {
     my $method = $self->method_lookup($self->{real_api_name});
     die unless $method;
 
-    local $OpenILS::Utils::CStoreEditor::always_xact = 1;
+    local $OpenILS::Utils::CStoreEditor::always_xact = use_authoritative();
 
     $client->respond( $_ ) for ( $method->run(@args) );
 
diff --git a/docs/RELEASE_NOTES_NEXT/Administration/authoritative.adoc b/docs/RELEASE_NOTES_NEXT/Administration/authoritative.adoc
new file mode 100644 (file)
index 0000000..be51caf
--- /dev/null
@@ -0,0 +1,22 @@
+== Optional Evergreen "Authoritative" API Support ==
+
+Previous versions of Evergreen supported "authoritative" API calls by default.
+These are API calls which force database reads to go the primary database
+instead of a pooled replica.
+
+Going forward, this functionality will be disabled by default, but may be
+enabled via a new opensrf.xml setting.
+
+If your site uses database pooling, with Evergreen actively reading from
+replicas, add this setting to your opensrf.xml file within the <default/>
+block:
+
+[source,xml]
+------------------------------------------------------------------------------
+<opensrf version='0.0.3'>
+  <default>
+    <uses_pooled_read_replica_dbs>true</uses_pooled_read_replica_dbs>
+<!-- ... -->
+------------------------------------------------------------------------------
+
+