Skip to content

Commit 0fe59c7

Browse files
authored
added ows_cache for GetCaps (#508)
1 parent dd9de01 commit 0fe59c7

File tree

8 files changed

+260
-18
lines changed

8 files changed

+260
-18
lines changed

mas/api/api.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,28 @@ func handler(response http.ResponseWriter, request *http.Request) {
137137
request.URL.Path,
138138
).Scan(&payload)
139139

140+
} else if _, ok := query["put_ows_cache"]; ok {
141+
err = db.QueryRow(
142+
`select mas_put_ows_cache(
143+
nullif($1,'')::text,
144+
nullif($2,'')::text,
145+
nullif($3,'')::jsonb
146+
) as json`,
147+
request.URL.Path,
148+
request.FormValue("query"),
149+
request.FormValue("value"),
150+
).Scan(&payload)
151+
152+
} else if _, ok := query["get_ows_cache"]; ok {
153+
err = db.QueryRow(
154+
`select mas_get_ows_cache(
155+
nullif($1,'')::text,
156+
nullif($2,'')::text
157+
) as json`,
158+
request.URL.Path,
159+
request.FormValue("query"),
160+
).Scan(&payload)
161+
140162
} else {
141163
httpJSONError(response, errors.New("unknown operation; currently supported: ?intersects, ?timestamps, ?extents"), 400)
142164
return

mas/api/mas.sql

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ create or replace function mas_timestamps(
589589
returns jsonb language plpgsql as $$
590590
declare
591591
result jsonb;
592-
query_hash text;
592+
query_hash uuid;
593593
shard text;
594594
begin
595595

@@ -605,16 +605,16 @@ create or replace function mas_timestamps(
605605
end if;
606606

607607
query_hash := md5(concat(gpath, coalesce(time_a::text, 'null'),
608-
coalesce(time_b::text, 'null'), array_to_string(namespace, ',', 'null')));
608+
coalesce(time_b::text, 'null'), array_to_string(namespace, ',', 'null')))::uuid;
609609

610-
if token is not null and token = query_hash then
611-
select jsonb_build_object('timestamps', '[]'::jsonb, 'token', query_hash) into result from timestamps_cache where query_id = query_hash;
610+
if token is not null and token = query_hash::text then
611+
select jsonb_build_object('timestamps', '[]'::jsonb, 'token', query_hash) into result from ows_cache where query_id = query_hash;
612612
if result is not null then
613613
return result;
614614
end if;
615615
end if;
616616

617-
select timestamps || jsonb_build_object('token', query_hash) into result from timestamps_cache where query_id = query_hash;
617+
select value || jsonb_build_object('token', query_hash) into result from ows_cache where query_id = query_hash;
618618
if result is not null then
619619
return result;
620620
end if;
@@ -657,7 +657,7 @@ create or replace function mas_timestamps(
657657

658658
), '[]'::jsonb), 'token', query_hash);
659659

660-
insert into timestamps_cache (query_id, timestamps) values (query_hash, result)
660+
insert into ows_cache (query_id, value) values (query_hash, result)
661661
on conflict (query_id) do nothing;
662662

663663
perform mas_reset();
@@ -666,6 +666,69 @@ create or replace function mas_timestamps(
666666
end
667667
$$;
668668

669+
create or replace function mas_put_ows_cache(
670+
gpath text,
671+
query text,
672+
val jsonb
673+
)
674+
returns jsonb language plpgsql as $$
675+
declare
676+
query_hash uuid;
677+
shard text;
678+
begin
679+
680+
if gpath is null then
681+
raise exception 'invalid search path';
682+
end if;
683+
684+
perform mas_reset();
685+
shard := mas_view(gpath);
686+
687+
if shard = '' then
688+
raise exception 'invalid search path';
689+
end if;
690+
691+
query_hash := md5(query)::uuid;
692+
insert into ows_cache (query_id, value) values (query_hash, val)
693+
on conflict (query_id) do update set value = val;
694+
695+
perform mas_reset();
696+
return jsonb_build_object('error', '');
697+
end
698+
$$;
699+
700+
create or replace function mas_get_ows_cache(
701+
gpath text,
702+
query text
703+
)
704+
returns jsonb language plpgsql as $$
705+
declare
706+
query_hash uuid;
707+
shard text;
708+
result jsonb;
709+
begin
710+
711+
if gpath is null then
712+
raise exception 'invalid search path';
713+
end if;
714+
715+
perform mas_reset();
716+
shard := mas_view(gpath);
717+
718+
if shard = '' then
719+
raise exception 'invalid search path';
720+
end if;
721+
722+
query_hash := md5(query)::uuid;
723+
724+
result := jsonb_build_object('value',
725+
(select value from ows_cache where query_id = query_hash));
726+
727+
perform mas_reset();
728+
return result;
729+
end
730+
$$;
731+
669732
-- Find geospatial and temporal extents
670733

671734
create or replace function mas_spatial_temporal_extents(

mas/db/shard.sql

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -660,19 +660,17 @@ create or replace function refresh_polygons()
660660
end
661661
$$;
662662

663-
drop table if exists timestamps_cache cascade;
664-
665-
-- cache for timestamps
666-
create unlogged table timestamps_cache (
667-
query_id text primary key,
668-
timestamps jsonb not null
663+
drop table if exists ows_cache cascade;
664+
create table ows_cache (
665+
query_id uuid primary key,
666+
value jsonb not null
669667
);
670668

671669
create or replace function refresh_caches()
672670
returns boolean language plpgsql as $$
673671
begin
674672
raise notice 'refresh caches';
675-
truncate timestamps_cache;
673+
truncate ows_cache;
676674
return true;
677675
end
678676
$$;

mas/db/shard_create.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ insert into public.shards (sh_code, sh_path)
3131
3232
\\i shard.sql
3333
34-
grant select,insert,update on ${shard}.timestamps_cache to api;
34+
grant select,insert,update on ${shard}.ows_cache to api;
3535
3636
EOD
3737
)

mas/db/shard_reset.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ alter default privileges for role mas in schema ${shard}_tmp grant select on tab
1515
1616
\\i shard.sql
1717
18-
grant select,insert,update on ${shard}_tmp.timestamps_cache to api;
18+
grant select,insert,update on ${shard}_tmp.ows_cache to api;
1919
2020
EOD
2121
)

ows.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,48 @@ func serveWMS(ctx context.Context, params utils.WMSParams, conf *utils.Config, r
186186
return
187187
}
188188

189-
newConf := conf.Copy(r)
190-
utils.LoadConfigTimestamps(newConf, *verbose)
189+
query := fmt.Sprintf("%swms_getcaps", r.URL.Path)
190+
gpath := utils.FindConfigGPath(conf)
191+
owsCache := utils.NewOWSCache(conf.ServiceConfig.MASAddress, gpath, *verbose)
192+
newConf, err := owsCache.GetConfig(query)
193+
cacheMiss := false
194+
if err != nil {
195+
if *verbose {
196+
log.Printf("WMS GetCapabilities get cache error: %v", err)
197+
}
198+
cacheMiss = true
199+
} else if newConf == nil {
200+
cacheMiss = true
201+
} else if len(newConf.Layers) == 0 {
202+
cacheMiss = true
203+
}
191204

192-
err := utils.ExecuteWriteTemplateFile(w, newConf,
205+
if cacheMiss {
206+
newConf = conf.Copy(r)
207+
utils.LoadConfigTimestamps(newConf, *verbose)
208+
}
209+
210+
err = utils.ExecuteWriteTemplateFile(w, newConf,
193211
utils.DataDir+"/templates/WMS_GetCapabilities.tpl")
194212
if err != nil {
195213
metricsCollector.Info.HTTPStatus = 500
196214
http.Error(w, err.Error(), 500)
197215
}
216+
217+
if cacheMiss {
218+
jsonBytes, mErr := json.Marshal(newConf)
219+
if mErr == nil {
220+
err = owsCache.Put(query, string(jsonBytes))
221+
if err != nil {
222+
if *verbose {
223+
log.Printf("WMS GetCapabilities put cache error: %v", err)
224+
}
225+
}
226+
} else {
227+
log.Printf("json.Marshal failed for WMS GetCapabilities")
228+
}
229+
}
230+
198231
case "GetFeatureInfo":
199232
x, y, err := utils.GetCoordinates(params)
200233
if err != nil {

utils/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ type ServiceConfig struct {
8080
TempDir string `json:"temp_dir"`
8181
MaxGrpcBufferSize int `json:"max_grpc_buffer_size"`
8282
EnableAutoLayers bool `json:"enable_auto_layers"`
83+
OWSCacheGPath string `json:"ows_cache_gpath"`
8384
}
8485

8586
type Mask struct {

utils/ows_cache.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package utils
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"net/http"
9+
"net/url"
10+
"sort"
11+
"strings"
12+
)
13+
14+
type OWSCache struct {
15+
MASAddress string
16+
GPath string
17+
verbose bool
18+
}
19+
20+
func NewOWSCache(masAddress, gpath string, verbose bool) *OWSCache {
21+
return &OWSCache{
22+
MASAddress: masAddress,
23+
GPath: gpath,
24+
verbose: verbose,
25+
}
26+
}
27+
28+
func (o *OWSCache) Put(query string, value string) error {
29+
reqURL := fmt.Sprintf("http://%s%s?put_ows_cache&query=%s", o.MASAddress, o.GPath, query)
30+
postBody := url.Values{"value": {value}}
31+
if o.verbose {
32+
log.Printf("querying MAS for OWSCache Put: %v", reqURL)
33+
}
34+
resp, err := http.PostForm(reqURL, postBody)
35+
if err != nil {
36+
return err
37+
}
38+
defer resp.Body.Close()
39+
40+
body, err := ioutil.ReadAll(resp.Body)
41+
if err != nil {
42+
return err
43+
}
44+
45+
type PutStatus struct {
46+
Error string `json:"error"`
47+
}
48+
49+
var status PutStatus
50+
err = json.Unmarshal(body, &status)
51+
if err != nil {
52+
return err
53+
}
54+
55+
if len(status.Error) > 0 {
56+
return fmt.Errorf("%s", status.Error)
57+
}
58+
return nil
59+
}
60+
61+
func (o *OWSCache) Get(query string) ([]byte, error) {
62+
var result []byte
63+
url := fmt.Sprintf("http://%s%s?get_ows_cache&query=%s", o.MASAddress, o.GPath, query)
64+
if o.verbose {
65+
log.Printf("querying MAS for OWSCache Get: %v", url)
66+
}
67+
68+
resp, err := http.Get(url)
69+
if err != nil {
70+
return result, err
71+
}
72+
defer resp.Body.Close()
73+
74+
result, err = ioutil.ReadAll(resp.Body)
75+
if err != nil {
76+
return result, err
77+
}
78+
return result, nil
79+
}
80+
81+
func (o *OWSCache) GetConfig(query string) (*Config, error) {
82+
value, err := o.Get(query)
83+
if err != nil {
84+
return nil, err
85+
}
86+
type cacheResult struct {
87+
Error string `json:"error"`
88+
Config *Config `json:"value"`
89+
}
90+
91+
var result cacheResult
92+
err = json.Unmarshal(value, &result)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
if len(result.Error) > 0 {
98+
return nil, fmt.Errorf("%s", result.Error)
99+
}
100+
return result.Config, nil
101+
}
102+
103+
func FindConfigGPath(config *Config) string {
104+
if len(strings.TrimSpace(config.ServiceConfig.OWSCacheGPath)) > 0 {
105+
return config.ServiceConfig.OWSCacheGPath
106+
}
107+
108+
if len(config.Layers) == 0 {
109+
return ""
110+
}
111+
var layerList []*Layer
112+
for iLayer := range config.Layers {
113+
layer := &config.Layers[iLayer]
114+
if hasBlendedService(layer) {
115+
continue
116+
}
117+
if len(strings.TrimSpace(layer.DataSource)) == 0 {
118+
continue
119+
}
120+
layerList = append(layerList, layer)
121+
}
122+
123+
sort.Slice(layerList, func(i, j int) bool { return layerList[i].Name <= layerList[j].Name })
124+
return layerList[0].DataSource
125+
}

0 commit comments

Comments
 (0)